diff options
1281 files changed, 26821 insertions, 9983 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index e18470498f39..56831d70af3a 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -23,7 +23,6 @@ aconfig_declarations_group { "aconfig_mediacodec_flags_java_lib", "aconfig_settingslib_flags_java_lib", "aconfig_trade_in_mode_flags_java_lib", - "android-sdk-flags-java", "android.adaptiveauth.flags-aconfig-java", "android.app.appfunctions.flags-aconfig-java", "android.app.assist.flags-aconfig-java", @@ -63,6 +62,7 @@ aconfig_declarations_group { "android.os.vibrator.flags-aconfig-java", "android.permission.flags-aconfig-java", "android.provider.flags-aconfig-java", + "android.sdk.flags-aconfig-java", "android.security.flags-aconfig-java", "android.server.app.flags-aconfig-java", "android.service.autofill.flags-aconfig-java", @@ -966,6 +966,13 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +java_aconfig_library { + name: "android.app.flags-aconfig-java-host", + aconfig_declarations: "android.app.flags-aconfig", + host_supported: true, + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Broadcast Radio aconfig_declarations { name: "android.hardware.radio.flags-aconfig", diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index a92a54337f96..d748a3bebfef 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -18,7 +18,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp tests/ tools/ bpfmt = -d -ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform +ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/android-sdk-flags/Android.bp b/android-sdk-flags/Android.bp index 79a0b9a4f273..d1df2ca69f50 100644 --- a/android-sdk-flags/Android.bp +++ b/android-sdk-flags/Android.bp @@ -17,14 +17,21 @@ package { } aconfig_declarations { - name: "android-sdk-flags", + name: "android.sdk.flags-aconfig", package: "android.sdk", container: "system", srcs: ["flags.aconfig"], } java_aconfig_library { - name: "android-sdk-flags-java", - aconfig_declarations: "android-sdk-flags", + name: "android.sdk.flags-aconfig-java", + aconfig_declarations: "android.sdk.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +java_aconfig_library { + name: "android.sdk.flags-aconfig-java-host", + aconfig_declarations: "android.sdk.flags-aconfig", + host_supported: true, defaults: ["framework-minus-apex-aconfig-java-defaults"], } diff --git a/apex/jobscheduler/service/aconfig/app_idle.aconfig b/apex/jobscheduler/service/aconfig/app_idle.aconfig index f079c02707e0..74d2a590086f 100644 --- a/apex/jobscheduler/service/aconfig/app_idle.aconfig +++ b/apex/jobscheduler/service/aconfig/app_idle.aconfig @@ -21,3 +21,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "adjust_default_bucket_elevation_params" + namespace: "backstage_power" + description: "Adjust the default bucket evaluation parameters" + bug: "379909479" +} 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 963307b110cf..a5a08fb9997c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -573,6 +573,7 @@ public class JobSchedulerService extends com.android.server.SystemService case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS: case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS: case Constants.KEY_SYSTEM_STOP_TO_FAILURE_RATIO: + case Constants.KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF: mConstants.updateBackoffConstantsLocked(); break; case Constants.KEY_CONN_CONGESTION_DELAY_FRAC: @@ -679,6 +680,8 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms"; private static final String KEY_SYSTEM_STOP_TO_FAILURE_RATIO = "system_stop_to_failure_ratio"; + private static final String KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = + "abandoned_job_timeouts_before_aggressive_backoff"; private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH = @@ -750,6 +753,7 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; private static final int DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO = 3; + private static final int DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = 3; private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true; @@ -845,7 +849,12 @@ public class JobSchedulerService extends com.android.server.SystemService * incremental failure in the backoff policy calculation. */ int SYSTEM_STOP_TO_FAILURE_RATIO = DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO; - + /** + * Number of consecutive timeouts by abandoned jobs before we change to aggressive backoff + * policy. + */ + int ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = + DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF; /** * The fraction of a job's running window that must pass before we * consider running it when the network is congested. @@ -1078,6 +1087,10 @@ public class JobSchedulerService extends com.android.server.SystemService SYSTEM_STOP_TO_FAILURE_RATIO = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_SYSTEM_STOP_TO_FAILURE_RATIO, DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO); + ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF, + DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF); } // TODO(141645789): move into ConnectivityController.CcConfig @@ -1287,6 +1300,8 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_SYSTEM_STOP_TO_FAILURE_RATIO, SYSTEM_STOP_TO_FAILURE_RATIO).println(); + pw.print(KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF, + ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println(); @@ -2997,6 +3012,7 @@ public class JobSchedulerService extends com.android.server.SystemService final long initialBackoffMillis = job.getInitialBackoffMillis(); int numFailures = failureToReschedule.getNumFailures(); + int numAbandonedFailures = failureToReschedule.getNumAbandonedFailures(); int numSystemStops = failureToReschedule.getNumSystemStops(); // We should back off slowly if JobScheduler keeps stopping the job, // but back off immediately if the issue appeared to be the app's fault @@ -3006,9 +3022,19 @@ public class JobSchedulerService extends com.android.server.SystemService || internalStopReason == JobParameters.INTERNAL_STOP_REASON_ANR || stopReason == JobParameters.STOP_REASON_USER) { numFailures++; + } else if (android.app.job.Flags.handleAbandonedJobs() + && internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED) { + numAbandonedFailures++; + numFailures++; } else { numSystemStops++; } + + int backoffPolicy = job.getBackoffPolicy(); + if (shouldUseAggressiveBackoff(numAbandonedFailures)) { + backoffPolicy = JobInfo.BACKOFF_POLICY_EXPONENTIAL; + } + final int backoffAttempts = numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; final long earliestRuntimeMs; @@ -3017,7 +3043,7 @@ public class JobSchedulerService extends com.android.server.SystemService earliestRuntimeMs = JobStatus.NO_EARLIEST_RUNTIME; } else { long delayMillis; - switch (job.getBackoffPolicy()) { + switch (backoffPolicy) { case JobInfo.BACKOFF_POLICY_LINEAR: { long backoff = initialBackoffMillis; if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) { @@ -3046,7 +3072,7 @@ public class JobSchedulerService extends com.android.server.SystemService } JobStatus newJob = new JobStatus(failureToReschedule, earliestRuntimeMs, - JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops, + JobStatus.NO_LATEST_RUNTIME, numFailures, numAbandonedFailures, numSystemStops, failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(), failureToReschedule.getCumulativeExecutionTimeMs()); if (stopReason == JobParameters.STOP_REASON_USER) { @@ -3069,6 +3095,20 @@ public class JobSchedulerService extends com.android.server.SystemService } /** + * Returns {@code true} if the given number of abandoned failures indicates that JobScheduler + * should use an aggressive backoff policy. + * + * @param numAbandonedFailures The number of abandoned failures. + * @return {@code true} if the given number of abandoned failures indicates that JobScheduler + * should use an aggressive backoff policy. + */ + public boolean shouldUseAggressiveBackoff(int numAbandonedFailures) { + return android.app.job.Flags.handleAbandonedJobs() + && numAbandonedFailures + > mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF; + } + + /** * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This * does not cause a job's period to be larger than requested (eg: if the requested period is * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene @@ -3147,6 +3187,7 @@ public class JobSchedulerService extends com.android.server.SystemService return new JobStatus(periodicToReschedule, elapsedNow + period - flex, elapsedNow + period, 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numAbandonedFailures */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); @@ -3163,6 +3204,7 @@ public class JobSchedulerService extends com.android.server.SystemService return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, newLatestRuntimeElapsed, 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numAbandonedFailures */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); @@ -3171,6 +3213,10 @@ public class JobSchedulerService extends com.android.server.SystemService @VisibleForTesting void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) { boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT; + if (android.app.job.Flags.handleAbandonedJobs()) { + jobTimedOut |= (debugStopReason + == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + } // If madeActive = 0, the job never actually started. if (!jobTimedOut && jobStatus.madeActive > 0) { final long executionDurationMs = sUptimeMillisClock.millis() - jobStatus.madeActive; @@ -3252,9 +3298,12 @@ public class JobSchedulerService extends com.android.server.SystemService // we stop it. final JobStatus rescheduledJob = needsReschedule ? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null; + final boolean isStopReasonAbandoned = android.app.job.Flags.handleAbandonedJobs() + && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); if (rescheduledJob != null && !rescheduledJob.shouldTreatAsUserInitiatedJob() && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT + || isStopReasonAbandoned || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) { rescheduledJob.disallowRunInBatterySaverAndDoze(); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index d8934d8f83b8..dfb36818c818 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -269,7 +269,9 @@ public final class JobStore { convertRtcBoundsToElapsed(utcTimes, elapsedNow); JobStatus newJob = new JobStatus(job, elapsedRuntimes.first, elapsedRuntimes.second, - 0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime(), + 0 /* numFailures */, 0 /* numAbandonedFailures */, + 0 /* numSystemStops */, job.getLastSuccessfulRunTime(), + job.getLastFailedRunTime(), job.getCumulativeExecutionTimeMs()); newJob.prepareLocked(); toAdd.add(newJob); 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 a3eaefd5f057..5a33aa0ab7eb 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 @@ -316,6 +316,12 @@ public final class JobStatus { private final int numFailures; /** + * How many times this job has stopped due to {@link + * JobParameters#STOP_REASON_TIMEOUT_ABANDONED}. + */ + private final int mNumAbandonedFailures; + + /** * The number of times JobScheduler has forced this job to stop due to reasons mostly outside * of the app's control. */ @@ -605,6 +611,8 @@ public final class JobStatus { * @param tag A string associated with the job for debugging/logging purposes. * @param numFailures Count of how many times this job has requested a reschedule because * its work was not yet finished. + * @param mNumAbandonedFailures Count of how many times this job has requested a reschedule + * because it was abandoned. * @param numSystemStops Count of how many times JobScheduler has forced this job to stop due to * factors mostly out of the app's control. * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job @@ -617,7 +625,7 @@ public final class JobStatus { */ private JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, - int numFailures, int numSystemStops, + int numFailures, int mNumAbandonedFailures, int numSystemStops, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, int internalFlags, @@ -677,6 +685,7 @@ public final class JobStatus { this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.numFailures = numFailures; + this.mNumAbandonedFailures = mNumAbandonedFailures; mNumSystemStops = numSystemStops; int requiredConstraints = job.getConstraintFlags(); @@ -750,7 +759,8 @@ public final class JobStatus { this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), jobStatus.getStandbyBucket(), jobStatus.getNamespace(), - jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(), + jobStatus.getSourceTag(), jobStatus.getNumFailures(), + jobStatus.getNumAbandonedFailures(), jobStatus.getNumSystemStops(), jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), jobStatus.getCumulativeExecutionTimeMs(), @@ -787,6 +797,7 @@ public final class JobStatus { this(job, callingUid, sourcePkgName, sourceUserId, standbyBucket, namespace, sourceTag, /* numFailures */ 0, /* numSystemStops */ 0, + /* mNumAbandonedFailures */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, innerFlags, dynamicConstraints); @@ -806,13 +817,15 @@ public final class JobStatus { /** Create a new job to be rescheduled with the provided parameters. */ public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, - long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, + long newLatestRuntimeElapsedMillis, int numFailures, + int mNumAbandonedFailures, int numSystemStops, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs) { this(rescheduling.job, rescheduling.getUid(), rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), rescheduling.getStandbyBucket(), rescheduling.getNamespace(), - rescheduling.getSourceTag(), numFailures, numSystemStops, + rescheduling.getSourceTag(), numFailures, + mNumAbandonedFailures, numSystemStops, newEarliestRuntimeElapsedMillis, newLatestRuntimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, @@ -851,7 +864,8 @@ public final class JobStatus { int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage, sourceUserId, elapsedNow); return new JobStatus(job, callingUid, sourcePkg, sourceUserId, - standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0, + standbyBucket, namespace, tag, /* numFailures */ 0, + /* mNumAbandonedFailures */ 0, /* numSystemStops */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, /* cumulativeExecutionTime */ 0, @@ -1146,6 +1160,13 @@ public final class JobStatus { } /** + * Returns the number of times the job stopped previously for STOP_REASON_TIMEOUT_ABANDONED. + */ + public int getNumAbandonedFailures() { + return mNumAbandonedFailures; + } + + /** * Returns the number of times the system stopped a previous execution of this job for reasons * that were likely outside the app's control. */ 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 c9d340757c6b..9871d713178e 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -337,11 +337,11 @@ public class AppStandbyController */ long[] mAppStandbyElapsedThresholds = DEFAULT_ELAPSED_TIME_THRESHOLDS; /** Minimum time a strong usage event should keep the bucket elevated. */ - long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_STRONG_USAGE_TIMEOUT; + long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT; /** Minimum time a notification seen event should keep the bucket elevated. */ long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT; /** Minimum time a slice pinned event should keep the bucket elevated. */ - long mSlicePinnedTimeoutMillis = ConstantsObserver.DEFAULT_SLICE_PINNED_TIMEOUT; + long mSlicePinnedTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT; /** The standby bucket that an app will be promoted on a notification-seen event */ int mNotificationSeenPromotedBucket = ConstantsObserver.DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET; @@ -362,7 +362,9 @@ public class AppStandbyController /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */ long mPredictionTimeoutMillis = DEFAULT_PREDICTION_TIMEOUT; /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */ - long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_SYNC_ADAPTER_TIMEOUT; + long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT; + /** The bucket that an app will be promoted on a sync adapter associated with a CP */ + int mSyncAdapaterPromotedBucket = STANDBY_BUCKET_ACTIVE; /** * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in * non-doze @@ -751,7 +753,7 @@ public class AppStandbyController userId); synchronized (mAppIdleLock) { reportNoninteractiveUsageCrossUserLocked(packageName, userId, - STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER, + mSyncAdapaterPromotedBucket, REASON_SUB_USAGE_SYNC_ADAPTER, elapsedRealtime, mSyncAdapterTimeoutMillis, linkedProfiles); } } @@ -2446,6 +2448,8 @@ public class AppStandbyController pw.println("Flags: "); pw.println(" " + Flags.FLAG_AVOID_IDLE_CHECK + ": " + Flags.avoidIdleCheck()); + pw.println(" " + Flags.FLAG_ADJUST_DEFAULT_BUCKET_ELEVATION_PARAMS + + ": " + Flags.adjustDefaultBucketElevationParams()); pw.println(); synchronized (mCarrierPrivilegedLock) { @@ -2481,6 +2485,9 @@ public class AppStandbyController pw.print(" mSyncAdapterTimeoutMillis="); TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw); pw.println(); + pw.print(" mSyncAdapaterPromotedBucket="); + pw.print(standbyBucketToString(mSyncAdapaterPromotedBucket)); + pw.println(); pw.print(" mSystemInteractionTimeoutMillis="); TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw); pw.println(); @@ -3059,12 +3066,18 @@ public class AppStandbyController public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS = COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR; - public static final long DEFAULT_STRONG_USAGE_TIMEOUT = + public static final long DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR; + + public static final long DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 5 * ONE_MINUTE; public static final long DEFAULT_NOTIFICATION_TIMEOUT = COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR; - public static final long DEFAULT_SLICE_PINNED_TIMEOUT = + public static final long DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT = COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR; + + public static final long DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT = + COMPRESS_TIME ? 12 * ONE_MINUTE : 2 * ONE_HOUR; public static final int DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET = STANDBY_BUCKET_WORKING_SET; public static final boolean DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS = false; @@ -3073,8 +3086,11 @@ public class AppStandbyController COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR; public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; - public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = + public static final long DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; + + public static final long DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR; public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = COMPRESS_TIME ? (ONE_MINUTE / 2) : 10 * ONE_MINUTE; public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = @@ -3117,6 +3133,9 @@ public class AppStandbyController cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED), false, this); mInjector.registerDeviceConfigPropertiesChangedListener(this); + + processDefaultConstants(); + // Load all the constants. // postOneTimeCheckIdleStates() doesn't need to be called on boot. processProperties(mInjector.getDeviceConfigProperties()); @@ -3135,6 +3154,17 @@ public class AppStandbyController postOneTimeCheckIdleStates(); } + private void processDefaultConstants() { + if (!Flags.adjustDefaultBucketElevationParams()) { + return; + } + + mSlicePinnedTimeoutMillis = DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT; + mSyncAdapterTimeoutMillis = DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT; + mSyncAdapaterPromotedBucket = STANDBY_BUCKET_WORKING_SET; + mStrongUsageTimeoutMillis = DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT; + } + private void processProperties(DeviceConfig.Properties properties) { boolean timeThresholdsUpdated = false; synchronized (mAppIdleLock) { @@ -3182,11 +3212,16 @@ public class AppStandbyController case KEY_SLICE_PINNED_HOLD_DURATION: mSlicePinnedTimeoutMillis = properties.getLong( KEY_SLICE_PINNED_HOLD_DURATION, - DEFAULT_SLICE_PINNED_TIMEOUT); + Flags.adjustDefaultBucketElevationParams() + ? DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT + : DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT); break; case KEY_STRONG_USAGE_HOLD_DURATION: mStrongUsageTimeoutMillis = properties.getLong( - KEY_STRONG_USAGE_HOLD_DURATION, DEFAULT_STRONG_USAGE_TIMEOUT); + KEY_STRONG_USAGE_HOLD_DURATION, + Flags.adjustDefaultBucketElevationParams() + ? DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT + : DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT); break; case KEY_PREDICTION_TIMEOUT: mPredictionTimeoutMillis = properties.getLong( @@ -3203,7 +3238,10 @@ public class AppStandbyController break; case KEY_SYNC_ADAPTER_HOLD_DURATION: mSyncAdapterTimeoutMillis = properties.getLong( - KEY_SYNC_ADAPTER_HOLD_DURATION, DEFAULT_SYNC_ADAPTER_TIMEOUT); + KEY_SYNC_ADAPTER_HOLD_DURATION, + Flags.adjustDefaultBucketElevationParams() + ? DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT + : DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT); break; case KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION: mExemptedSyncScheduledDozeTimeoutMillis = properties.getLong( diff --git a/core/api/current.txt b/core/api/current.txt index 3af8e7e17dbd..6aef9dd83085 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1203,6 +1203,7 @@ package android { field public static final int minResizeHeight = 16843670; // 0x1010396 field public static final int minResizeWidth = 16843669; // 0x1010395 field public static final int minSdkVersion = 16843276; // 0x101020c + field @FlaggedApi("android.content.pm.support_minor_versions_in_minsdkversion") public static final int minSdkVersionFull; field public static final int minWidth = 16843071; // 0x101013f field public static final int minimumHorizontalAngle = 16843901; // 0x101047d field public static final int minimumVerticalAngle = 16843902; // 0x101047e @@ -10089,10 +10090,10 @@ package android.companion { method @FlaggedApi("android.companion.unpair_associated_device") @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond(int); method public void requestNotificationAccess(android.content.ComponentName); method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String); - method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException; + method @Deprecated @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException; method @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull android.companion.ObservingDevicePresenceRequest); method public void startSystemDataTransfer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.companion.CompanionException>) throws android.companion.DeviceNotAssociatedException; - method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException; + method @Deprecated @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException; method @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull android.companion.ObservingDevicePresenceRequest); field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION"; field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE"; @@ -10120,9 +10121,9 @@ package android.companion { method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException; method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String); - method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo); + method @Deprecated @FlaggedApi("android.companion.device_presence") @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo); method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String); - method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo); + method @Deprecated @FlaggedApi("android.companion.device_presence") @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo); method @FlaggedApi("android.companion.device_presence") @MainThread public void onDevicePresenceEvent(@NonNull android.companion.DevicePresenceEvent); field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService"; } @@ -13225,8 +13226,8 @@ package android.content.pm { public abstract class PackageManager { ctor @Deprecated public PackageManager(); method @Deprecated public abstract void addPackageToPreferred(@NonNull String); - method public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo); - method public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo); + method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo); + method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo); method @Deprecated public abstract void addPreferredActivity(@NonNull android.content.IntentFilter, int, @Nullable android.content.ComponentName[], @NonNull android.content.ComponentName); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean addWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); method public boolean canPackageQuery(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; @@ -13367,7 +13368,7 @@ package android.content.pm { method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryServiceProperty(@NonNull String); method public void relinquishUpdateOwnership(@NonNull String); method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); - method public abstract void removePermission(@NonNull String); + method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract void removePermission(@NonNull String); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int); @@ -33612,9 +33613,14 @@ package android.os { @FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams { ctor public CpuHeadroomParams(); method public int getCalculationType(); + method @IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public long getCalculationWindowMillis(); method public void setCalculationType(int); + method public void setCalculationWindowMillis(@IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int); + method public void setTids(@NonNull int...); field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1 field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0 + field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710 + field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32 } public final class CpuUsageInfo implements android.os.Parcelable { @@ -33867,9 +33873,13 @@ package android.os { @FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams { ctor public GpuHeadroomParams(); method public int getCalculationType(); + method @IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public int getCalculationWindowMillis(); method public void setCalculationType(int); + method public void setCalculationWindowMillis(@IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int); field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1 field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0 + field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710 + field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32 } public class Handler { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4b83b43bfd8f..83699ac30939 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3940,6 +3940,7 @@ package android.content { field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; + field @FlaggedApi("android.net.wifi.flags.usd") public static final String WIFI_USD_SERVICE = "wifi_usd"; } public final class ContextParams { @@ -5209,6 +5210,11 @@ package android.hardware.contexthub { method @NonNull public java.util.Collection<android.hardware.contexthub.HubServiceInfo> getServiceInfoCollection(); method @Nullable public String getTag(); method public int getVersion(); + field public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; // 0x4 + field public static final int REASON_ENDPOINT_INVALID = 5; // 0x5 + field public static final int REASON_ENDPOINT_STOPPED = 6; // 0x6 + field public static final int REASON_FAILURE = 0; // 0x0 + field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3 } public static final class HubEndpoint.Builder { @@ -5295,16 +5301,13 @@ package android.hardware.contexthub { @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointDiscoveryCallback { method public void onEndpointsStarted(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>); - method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>); + method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>, int); } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback { method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int); method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable android.hardware.contexthub.HubServiceInfo); method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession); - field public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; // 0x4 - field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3 - field public static final int REASON_UNSPECIFIED = 0; // 0x0 } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback { @@ -7634,10 +7637,11 @@ package android.media { method public boolean isActive(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted(); method public boolean isSpatialized(); - field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8 + field @Deprecated @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1 field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_OP_CONTROL_AUDIO = 128; // 0x80 + field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_OP_PLAY_AUDIO = 8; // 0x8 field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_PORT_VOLUME = 64; // 0x40 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_MUTED = 4; // 0x4 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_VOLUME = 2; // 0x2 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 967f6194969e..3a62ac9e3c62 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -541,7 +541,9 @@ package android.app { method @Nullable public android.graphics.Rect peekBitmapDimensions(); method @Nullable public android.graphics.Rect peekBitmapDimensions(int); method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithCrops(@Nullable android.graphics.Bitmap, @NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>, boolean, int) throws java.io.IOException; + method @FlaggedApi("android.app.live_wallpaper_content_handling") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithDescription(@Nullable android.graphics.Bitmap, @NonNull android.app.wallpaper.WallpaperDescription, boolean, int) throws java.io.IOException; method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(@NonNull java.io.InputStream, @NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>, boolean, int) throws java.io.IOException; + method @FlaggedApi("android.app.live_wallpaper_content_handling") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithDescription(@NonNull java.io.InputStream, @NonNull android.app.wallpaper.WallpaperDescription, boolean, int) throws java.io.IOException; method public void setWallpaperZoomOut(@NonNull android.os.IBinder, float); method public boolean shouldEnableWideColorGamut(); method public boolean wallpaperSupportsWcg(int); @@ -907,6 +909,15 @@ package android.app.usage { } +package android.app.wallpaper { + + public static final class WallpaperDescription.Builder { + method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>); + method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull android.util.SparseArray<android.graphics.Rect>); + } + +} + package android.appwidget { public class AppWidgetManager { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7e0a9b69b7bd..3cbea87e135e 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -130,7 +130,6 @@ import android.util.Slog; import android.util.Xml; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.Immutable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.pm.RoSystemFeatures; @@ -1020,6 +1019,33 @@ public class ApplicationPackageManager extends PackageManager { } } + @Override + public void setPageSizeAppCompatFlagsSettingsOverride(String packageName, boolean enabled) { + try { + mPM.setPageSizeAppCompatFlagsSettingsOverride(packageName, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean isPageSizeCompatEnabled(String packageName) { + try { + return mPM.isPageSizeCompatEnabled(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String getPageSizeCompatWarningMessage(String packageName) { + try { + return mPM.getPageSizeCompatWarningMessage(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private static List<byte[]> encodeCertificates(List<Certificate> certs) throws CertificateEncodingException { if (certs == null) { diff --git a/core/java/android/app/BroadcastStickyCache.java b/core/java/android/app/BroadcastStickyCache.java new file mode 100644 index 000000000000..fe2e10752355 --- /dev/null +++ b/core/java/android/app/BroadcastStickyCache.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.Context.RegisterReceiverFlags; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbManager; +import android.media.AudioManager; +import android.net.ConnectivityManager; +import android.net.TetheringManager; +import android.net.nsd.NsdManager; +import android.net.wifi.WifiManager; +import android.net.wifi.p2p.WifiP2pManager; +import android.os.IpcDataCache; +import android.os.IpcDataCache.Config; +import android.os.UpdateLock; +import android.telephony.TelephonyManager; +import android.util.ArrayMap; +import android.view.WindowManagerPolicyConstants; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; + +/** @hide */ +public class BroadcastStickyCache { + + @VisibleForTesting + public static final String[] STICKY_BROADCAST_ACTIONS = { + AudioManager.ACTION_HDMI_AUDIO_PLUG, + AudioManager.ACTION_HEADSET_PLUG, + AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED, + AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED, + AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, + AudioManager.RINGER_MODE_CHANGED_ACTION, + ConnectivityManager.CONNECTIVITY_ACTION, + Intent.ACTION_BATTERY_CHANGED, + Intent.ACTION_DEVICE_STORAGE_FULL, + Intent.ACTION_DEVICE_STORAGE_LOW, + Intent.ACTION_SIM_STATE_CHANGED, + NsdManager.ACTION_NSD_STATE_CHANGED, + TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED, + TetheringManager.ACTION_TETHER_STATE_CHANGED, + UpdateLock.UPDATE_LOCK_CHANGED, + UsbManager.ACTION_USB_STATE, + WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED, + WifiManager.NETWORK_STATE_CHANGED_ACTION, + WifiManager.SUPPLICANT_STATE_CHANGED_ACTION, + WifiManager.WIFI_STATE_CHANGED_ACTION, + WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION, + WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED, + "android.net.conn.INET_CONDITION_ACTION" // ConnectivityManager.INET_CONDITION_ACTION + }; + + @VisibleForTesting + public static final ArrayMap<String, String> sActionApiNameMap = new ArrayMap<>(); + + private static final ArrayMap<String, IpcDataCache.Config> sActionConfigMap = new ArrayMap<>(); + + private static final ArrayMap<StickyBroadcastFilter, IpcDataCache<Void, Intent>> + sFilterCacheMap = new ArrayMap<>(); + + static { + sActionApiNameMap.put(AudioManager.ACTION_HDMI_AUDIO_PLUG, "hdmi_audio_plug"); + sActionApiNameMap.put(AudioManager.ACTION_HEADSET_PLUG, "headset_plug"); + sActionApiNameMap.put(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED, + "sco_audio_state_changed"); + sActionApiNameMap.put(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED, + "action_sco_audio_state_updated"); + sActionApiNameMap.put(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, + "internal_ringer_mode_changed_action"); + sActionApiNameMap.put(AudioManager.RINGER_MODE_CHANGED_ACTION, + "ringer_mode_changed"); + sActionApiNameMap.put(ConnectivityManager.CONNECTIVITY_ACTION, + "connectivity_change"); + sActionApiNameMap.put(Intent.ACTION_BATTERY_CHANGED, "battery_changed"); + sActionApiNameMap.put(Intent.ACTION_DEVICE_STORAGE_FULL, "device_storage_full"); + sActionApiNameMap.put(Intent.ACTION_DEVICE_STORAGE_LOW, "device_storage_low"); + sActionApiNameMap.put(Intent.ACTION_SIM_STATE_CHANGED, "sim_state_changed"); + sActionApiNameMap.put(NsdManager.ACTION_NSD_STATE_CHANGED, "nsd_state_changed"); + sActionApiNameMap.put(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED, + "service_providers_updated"); + sActionApiNameMap.put(TetheringManager.ACTION_TETHER_STATE_CHANGED, + "tether_state_changed"); + sActionApiNameMap.put(UpdateLock.UPDATE_LOCK_CHANGED, "update_lock_changed"); + sActionApiNameMap.put(UsbManager.ACTION_USB_STATE, "usb_state"); + sActionApiNameMap.put(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED, + "wifi_scan_availability_changed"); + sActionApiNameMap.put(WifiManager.NETWORK_STATE_CHANGED_ACTION, + "network_state_change"); + sActionApiNameMap.put(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION, + "supplicant_state_change"); + sActionApiNameMap.put(WifiManager.WIFI_STATE_CHANGED_ACTION, "wifi_state_changed"); + sActionApiNameMap.put( + WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION, "wifi_p2p_state_changed"); + sActionApiNameMap.put( + WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED, "hdmi_plugged"); + sActionApiNameMap.put( + "android.net.conn.INET_CONDITION_ACTION", "inet_condition_action"); + } + + /** + * Checks whether we can use caching for the given filter. + */ + public static boolean useCache(@Nullable IntentFilter filter) { + return Flags.useStickyBcastCache() + && filter != null + && filter.safeCountActions() == 1 + && ArrayUtils.contains(STICKY_BROADCAST_ACTIONS, filter.getAction(0)); + } + + public static void invalidateCache(@NonNull String action) { + if (!Flags.useStickyBcastCache() + || !ArrayUtils.contains(STICKY_BROADCAST_ACTIONS, action)) { + return; + } + IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, + sActionApiNameMap.get(action)); + } + + public static void invalidateAllCaches() { + for (int i = sActionApiNameMap.size() - 1; i >= 0; i--) { + IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, + sActionApiNameMap.valueAt(i)); + } + } + + /** + * Returns the cached {@link Intent} based on the filter, if exits otherwise + * fetches the value from the service. + */ + @Nullable + public static Intent getIntent( + @NonNull IApplicationThread applicationThread, + @NonNull String mBasePackageName, + @Nullable String attributionTag, + @NonNull IntentFilter filter, + @Nullable String broadcastPermission, + @UserIdInt int userId, + @RegisterReceiverFlags int flags) { + IpcDataCache<Void, Intent> intentDataCache = findIpcDataCache(filter); + + if (intentDataCache == null) { + final String action = filter.getAction(0); + final StickyBroadcastFilter stickyBroadcastFilter = + new StickyBroadcastFilter(filter, action); + final Config config = getConfig(action); + + intentDataCache = + new IpcDataCache<>(config, + (query) -> ActivityManager.getService().registerReceiverWithFeature( + applicationThread, + mBasePackageName, + attributionTag, + /* receiverId= */ "null", + /* receiver= */ null, + filter, + broadcastPermission, + userId, + flags)); + sFilterCacheMap.put(stickyBroadcastFilter, intentDataCache); + } + return intentDataCache.query(null); + } + + @VisibleForTesting + public static void clearCacheForTest() { + sFilterCacheMap.clear(); + } + + @Nullable + private static IpcDataCache<Void, Intent> findIpcDataCache( + @NonNull IntentFilter filter) { + for (int i = sFilterCacheMap.size() - 1; i >= 0; i--) { + StickyBroadcastFilter existingFilter = sFilterCacheMap.keyAt(i); + if (filter.getAction(0).equals(existingFilter.action()) + && IntentFilter.filterEquals(existingFilter.filter(), filter)) { + return sFilterCacheMap.valueAt(i); + } + } + return null; + } + + @NonNull + private static IpcDataCache.Config getConfig(@NonNull String action) { + if (!sActionConfigMap.containsKey(action)) { + // We only need 1 entry per cache but just to be on the safer side we are choosing 32 + // although we don't expect more than 1. + sActionConfigMap.put(action, + new Config(32, IpcDataCache.MODULE_SYSTEM, sActionApiNameMap.get(action))); + } + + return sActionConfigMap.get(action); + } + + @VisibleForTesting + private record StickyBroadcastFilter(@NonNull IntentFilter filter, @NonNull String action) { + } +} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index cd56957ed5d1..dcbdc2348fbc 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1922,10 +1922,23 @@ class ContextImpl extends Context { } } try { - final Intent intent = ActivityManager.getService().registerReceiverWithFeature( - mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), - AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId, - flags); + final Intent intent; + if (receiver == null && BroadcastStickyCache.useCache(filter)) { + intent = BroadcastStickyCache.getIntent( + mMainThread.getApplicationThread(), + mBasePackageName, + getAttributionTag(), + filter, + broadcastPermission, + userId, + flags); + } else { + intent = ActivityManager.getService().registerReceiverWithFeature( + mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), + AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, + userId, flags); + } + if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); // TODO: determine at registration time if caller is diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0e68cce7a8f5..e2d20cb4b7dc 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -441,8 +441,8 @@ public class Notification implements Parcelable /** * A large-format version of {@link #contentView}, giving the Notification an - * opportunity to show more detail. The system UI may choose to show this - * instead of the normal content view at its discretion. + * opportunity to show more detail when expanded. The system UI may choose + * to show this instead of the normal content view at its discretion. * * As of N, this field may be null. The expanded notification view is determined by the * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be @@ -817,16 +817,16 @@ public class Notification implements Parcelable R.layout.notification_2025_template_expanded_base, R.layout.notification_2025_template_heads_up_base, R.layout.notification_2025_template_header, + R.layout.notification_2025_template_conversation, + R.layout.notification_2025_template_collapsed_call, + R.layout.notification_2025_template_expanded_call, + R.layout.notification_2025_template_collapsed_messaging, + R.layout.notification_2025_template_collapsed_media, R.layout.notification_template_material_big_picture, R.layout.notification_template_material_big_text, R.layout.notification_template_material_inbox, - R.layout.notification_template_material_messaging, R.layout.notification_template_material_big_messaging, - R.layout.notification_template_material_conversation, - R.layout.notification_template_material_media, R.layout.notification_template_material_big_media, - R.layout.notification_template_material_call, - R.layout.notification_template_material_big_call, R.layout.notification_template_header -> true; case R.layout.notification_template_material_progress -> Flags.apiRichOngoing(); default -> false; @@ -1337,7 +1337,7 @@ public class Notification implements Parcelable public static final String EXTRA_SUMMARY_TEXT = "android.summaryText"; /** - * {@link #extras} key: this is the longer text shown in the big form of a + * {@link #extras} key: this is the longer text shown in the expanded form of a * {@link BigTextStyle} notification, as supplied to * {@link BigTextStyle#bigText(CharSequence)}. */ @@ -5919,12 +5919,12 @@ public class Notification implements Parcelable private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p, TemplateBindResult result) { - p.headerless(resId == getBaseLayoutResource() + p.headerless(resId == getCollapsedBaseLayoutResource() || resId == getHeadsUpBaseLayoutResource() || resId == getCompactHeadsUpBaseLayoutResource() || resId == getMessagingCompactHeadsUpLayoutResource() - || resId == getMessagingLayoutResource() - || resId == R.layout.notification_template_material_media); + || resId == getCollapsedMessagingLayoutResource() + || resId == getCollapsedMediaLayoutResource()); RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId); resetStandardTemplate(contentView); @@ -6378,7 +6378,7 @@ public class Notification implements Parcelable boolean hideSnoozeButton = mN.isFgsOrUij() || mN.fullScreenIntent != null || isBackgroundColorized(p) - || p.mViewType != StandardTemplateParams.VIEW_TYPE_BIG; + || p.mViewType != StandardTemplateParams.VIEW_TYPE_EXPANDED; big.setBoolean(R.id.snooze_button, "setEnabled", !hideSnoozeButton); if (hideSnoozeButton) { // Only hide; NotificationContentView will show it when it adds the click listener @@ -6583,19 +6583,21 @@ public class Notification implements Parcelable .decorationType(StandardTemplateParams.DECORATION_MINIMAL) .fillTextsFrom(this); TemplateBindResult result = new TemplateBindResult(); - RemoteViews standard = applyStandardTemplate(getBaseLayoutResource(), p, result); + RemoteViews standard = applyStandardTemplate(getCollapsedBaseLayoutResource(), + p, result); buildCustomContentIntoTemplate(mContext, standard, customContent, p, result); return standard; } - private RemoteViews minimallyDecoratedBigContentView(@NonNull RemoteViews customContent) { + private RemoteViews minimallyDecoratedExpandedContentView( + @NonNull RemoteViews customContent) { StandardTemplateParams p = mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .decorationType(StandardTemplateParams.DECORATION_MINIMAL) .fillTextsFrom(this); TemplateBindResult result = new TemplateBindResult(); - RemoteViews standard = applyStandardTemplateWithActions(getBigBaseLayoutResource(), + RemoteViews standard = applyStandardTemplateWithActions(getExpandedBaseLayoutResource(), p, result); buildCustomContentIntoTemplate(mContext, standard, customContent, p, result); @@ -6641,7 +6643,7 @@ public class Notification implements Parcelable StandardTemplateParams p = mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) .fillTextsFrom(this); - return applyStandardTemplate(getBaseLayoutResource(), p, null /* result */); + return applyStandardTemplate(getCollapsedBaseLayoutResource(), p, null /* result */); } private boolean useExistingRemoteView(RemoteViews customContent) { @@ -6679,24 +6681,29 @@ public class Notification implements Parcelable */ @Deprecated public RemoteViews createBigContentView() { + return createExpandedContentView(); + } + + private RemoteViews createExpandedContentView() { RemoteViews result = null; if (useExistingRemoteView(mN.bigContentView)) { return fullyCustomViewRequiresDecoration(false /* fromStyle */) - ? minimallyDecoratedBigContentView(mN.bigContentView) : mN.bigContentView; + ? minimallyDecoratedExpandedContentView(mN.bigContentView) + : mN.bigContentView; } if (mStyle != null) { - result = mStyle.makeBigContentView(); + result = mStyle.makeExpandedContentView(); if (fullyCustomViewRequiresDecoration(true /* fromStyle */)) { - result = minimallyDecoratedBigContentView(result); + result = minimallyDecoratedExpandedContentView(result); } } if (result == null) { - if (bigContentViewRequired()) { + if (expandedContentViewRequired()) { StandardTemplateParams p = mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .allowTextWithProgress(true) .fillTextsFrom(this); - result = applyStandardTemplateWithActions(getBigBaseLayoutResource(), p, + result = applyStandardTemplateWithActions(getExpandedBaseLayoutResource(), p, null /* result */); } } @@ -6710,7 +6717,7 @@ public class Notification implements Parcelable // apps can detect the change, it's most likely that the changes will simply result in // visual regressions. @SuppressWarnings("AndroidFrameworkCompatChange") - private boolean bigContentViewRequired() { + private boolean expandedContentViewRequired() { if (Flags.notificationExpansionOptional()) { // Notifications without a bigContentView, style, or actions do not need to expand boolean exempt = mN.bigContentView == null @@ -7521,7 +7528,7 @@ public class Notification implements Parcelable } @UnsupportedAppUsage - private int getBaseLayoutResource() { + private int getCollapsedBaseLayoutResource() { if (Flags.notificationsRedesignTemplates()) { return R.layout.notification_2025_template_collapsed_base; } else { @@ -7545,7 +7552,7 @@ public class Notification implements Parcelable return R.layout.notification_template_material_messaging_compact_heads_up; } - private int getBigBaseLayoutResource() { + private int getExpandedBaseLayoutResource() { if (Flags.notificationsRedesignTemplates()) { return R.layout.notification_2025_template_expanded_base; } else { @@ -7565,16 +7572,48 @@ public class Notification implements Parcelable return R.layout.notification_template_material_inbox; } - private int getMessagingLayoutResource() { - return R.layout.notification_template_material_messaging; + private int getCollapsedMessagingLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_collapsed_messaging; + } else { + return R.layout.notification_template_material_messaging; + } } - private int getBigMessagingLayoutResource() { + private int getExpandedMessagingLayoutResource() { return R.layout.notification_template_material_big_messaging; } + private int getCollapsedMediaLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_collapsed_media; + } else { + return R.layout.notification_template_material_media; + } + } + private int getConversationLayoutResource() { - return R.layout.notification_template_material_conversation; + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_conversation; + } else { + return R.layout.notification_template_material_conversation; + } + } + + private int getCollapsedCallLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_collapsed_call; + } else { + return R.layout.notification_template_material_call; + } + } + + private int getExpandedCallLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_expanded_call; + } else { + return R.layout.notification_template_material_big_call; + } } private int getProgressLayoutResource() { @@ -8038,7 +8077,7 @@ public class Notification implements Parcelable protected Builder mBuilder; /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ protected void internalSetBigContentTitle(CharSequence title) { @@ -8046,7 +8085,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ protected void internalSetSummaryText(CharSequence cs) { mSummaryText = cs; @@ -8109,10 +8148,10 @@ public class Notification implements Parcelable } /** - * Construct a Style-specific RemoteViews for the final big notification layout. + * Construct a Style-specific RemoteViews for the final expanded notification layout. * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { return null; } @@ -8276,7 +8315,7 @@ public class Notification implements Parcelable } /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ @NonNull @@ -8286,7 +8325,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ @NonNull public BigPictureStyle setSummaryText(@Nullable CharSequence cs) { @@ -8345,7 +8384,7 @@ public class Notification implements Parcelable } /** - * Override the large icon when the big notification is shown. + * Override the large icon when the expanded notification is shown. */ @NonNull public BigPictureStyle bigLargeIcon(@Nullable Bitmap b) { @@ -8353,7 +8392,7 @@ public class Notification implements Parcelable } /** - * Override the large icon when the big notification is shown. + * Override the large icon when the expanded notification is shown. */ @NonNull public BigPictureStyle bigLargeIcon(@Nullable Icon icon) { @@ -8417,7 +8456,7 @@ public class Notification implements Parcelable .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) .fillTextsFrom(mBuilder) .promotedPicture(mPictureIcon); - return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */); + return getStandardView(mBuilder.getCollapsedBaseLayoutResource(), p, null /* result */); } /** @@ -8439,7 +8478,7 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet // This covers the following cases: // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides @@ -8458,7 +8497,7 @@ public class Notification implements Parcelable } StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG).fillTextsFrom(mBuilder); + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED).fillTextsFrom(mBuilder); RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(), p, null /* result */); if (mSummaryTextSet) { @@ -8605,7 +8644,7 @@ public class Notification implements Parcelable } /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ public BigTextStyle setBigContentTitle(CharSequence title) { @@ -8614,7 +8653,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ public BigTextStyle setSummaryText(CharSequence cs) { internalSetSummaryText(safeCharSequence(cs)); @@ -8622,7 +8661,7 @@ public class Notification implements Parcelable } /** - * Provide the longer text to be displayed in the big form of the + * Provide the longer text to be displayed in the expanded form of the * template in place of the content text. */ public BigTextStyle bigText(CharSequence cs) { @@ -8666,7 +8705,7 @@ public class Notification implements Parcelable if (increasedHeight) { ArrayList<Action> originalActions = mBuilder.mActions; mBuilder.mActions = new ArrayList<>(); - RemoteViews remoteViews = makeBigContentView(); + RemoteViews remoteViews = makeExpandedContentView(); mBuilder.mActions = originalActions; return remoteViews; } @@ -8680,7 +8719,7 @@ public class Notification implements Parcelable public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { if (increasedHeight && mBuilder.mActions.size() > 0) { // TODO(b/163626038): pass VIEW_TYPE_HEADS_UP? - return makeBigContentView(); + return makeExpandedContentView(); } return super.makeHeadsUpContentView(increasedHeight); } @@ -8688,9 +8727,9 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .allowTextWithProgress(true) .textViewId(R.id.big_text) .fillTextsFrom(mBuilder); @@ -9362,20 +9401,20 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { - return makeMessagingView(StandardTemplateParams.VIEW_TYPE_BIG); + public RemoteViews makeExpandedContentView() { + return makeMessagingView(StandardTemplateParams.VIEW_TYPE_EXPANDED); } /** * Create a messaging layout. * - * @param viewType one of StandardTemplateParams.VIEW_TYPE_NORMAL, VIEW_TYPE_BIG, + * @param viewType one of StandardTemplateParams.VIEW_TYPE_NORMAL, VIEW_TYPE_EXPANDEDIG, * VIEW_TYPE_HEADS_UP * @return the created remoteView. */ @NonNull private RemoteViews makeMessagingView(int viewType) { - boolean isCollapsed = viewType != StandardTemplateParams.VIEW_TYPE_BIG; + boolean isCollapsed = viewType != StandardTemplateParams.VIEW_TYPE_EXPANDED; boolean hideRightIcons = viewType != StandardTemplateParams.VIEW_TYPE_NORMAL; boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY; boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT; @@ -9419,8 +9458,8 @@ public class Notification implements Parcelable isConversationLayout ? mBuilder.getConversationLayoutResource() : isCollapsed - ? mBuilder.getMessagingLayoutResource() - : mBuilder.getBigMessagingLayoutResource(), + ? mBuilder.getCollapsedMessagingLayoutResource() + : mBuilder.getExpandedMessagingLayoutResource(), p, bindResult); if (isConversationLayout) { @@ -10043,7 +10082,7 @@ public class Notification implements Parcelable } /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ public InboxStyle setBigContentTitle(CharSequence title) { @@ -10052,7 +10091,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ public InboxStyle setSummaryText(CharSequence cs) { internalSetSummaryText(safeCharSequence(cs)); @@ -10100,9 +10139,9 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .fillTextsFrom(mBuilder).text(null); TemplateBindResult result = new TemplateBindResult(); RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), p, result); @@ -10357,8 +10396,8 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { - return makeMediaBigContentView(null /* customContent */); + public RemoteViews makeExpandedContentView() { + return makeMediaExpandedContentView(null /* customContent */); } /** @@ -10473,7 +10512,7 @@ public class Notification implements Parcelable .fillTextsFrom(mBuilder); TemplateBindResult result = new TemplateBindResult(); RemoteViews template = mBuilder.applyStandardTemplate( - R.layout.notification_template_material_media, p, + mBuilder.getCollapsedMediaLayoutResource(), p, null /* result */); for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) { @@ -10494,10 +10533,10 @@ public class Notification implements Parcelable } /** @hide */ - protected RemoteViews makeMediaBigContentView(@Nullable RemoteViews customContent) { + protected RemoteViews makeMediaExpandedContentView(@Nullable RemoteViews customContent) { final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS); StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .hideProgress(true) .fillTextsFrom(mBuilder); TemplateBindResult result = new TemplateBindResult(); @@ -10803,8 +10842,8 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { - return makeCallLayout(StandardTemplateParams.VIEW_TYPE_BIG); + public RemoteViews makeExpandedContentView() { + return makeCallLayout(StandardTemplateParams.VIEW_TYPE_EXPANDED); } @NonNull @@ -10925,10 +10964,10 @@ public class Notification implements Parcelable final RemoteViews contentView; if (isCollapsed) { contentView = mBuilder.applyStandardTemplate( - R.layout.notification_template_material_call, p, null /* result */); + mBuilder.getCollapsedCallLayoutResource(), p, null /* result */); } else { contentView = mBuilder.applyStandardTemplateWithActions( - R.layout.notification_template_material_big_call, p, null /* result */); + mBuilder.getExpandedCallLayoutResource(), p, null /* result */); } // Bind some extra conversation-specific header fields. @@ -11550,7 +11589,7 @@ public class Notification implements Parcelable .hideProgress(true) .fillTextsFrom(mBuilder); - return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */); + return getStandardView(mBuilder.getCollapsedBaseLayoutResource(), p, null /* result */); } /** * @hide @@ -11568,9 +11607,9 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .allowTextWithProgress(true) .hideProgress(true) .fillTextsFrom(mBuilder); @@ -12014,8 +12053,8 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { - return makeDecoratedBigContentView(); + public RemoteViews makeExpandedContentView() { + return makeDecoratedExpandedContentView(); } /** @@ -12058,13 +12097,13 @@ public class Notification implements Parcelable .decorationType(StandardTemplateParams.DECORATION_PARTIAL) .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplate( - mBuilder.getBaseLayoutResource(), p, result); + mBuilder.getCollapsedBaseLayoutResource(), p, result); buildCustomContentIntoTemplate(mBuilder.mContext, remoteViews, customContent, p, result); return remoteViews; } - private RemoteViews makeDecoratedBigContentView() { + private RemoteViews makeDecoratedExpandedContentView() { RemoteViews bigContentView = mBuilder.mN.bigContentView == null ? mBuilder.mN.contentView : mBuilder.mN.bigContentView; @@ -12073,11 +12112,11 @@ public class Notification implements Parcelable } TemplateBindResult result = new TemplateBindResult(); StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .decorationType(StandardTemplateParams.DECORATION_PARTIAL) .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions( - mBuilder.getBigBaseLayoutResource(), p, result); + mBuilder.getExpandedBaseLayoutResource(), p, result); buildCustomContentIntoTemplate(mBuilder.mContext, remoteViews, bigContentView, p, result); return remoteViews; @@ -12150,11 +12189,11 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { RemoteViews customContent = mBuilder.mN.bigContentView != null ? mBuilder.mN.bigContentView : mBuilder.mN.contentView; - return makeMediaBigContentView(customContent); + return makeMediaExpandedContentView(customContent); } /** @@ -12165,7 +12204,7 @@ public class Notification implements Parcelable RemoteViews customContent = mBuilder.mN.headsUpContentView != null ? mBuilder.mN.headsUpContentView : mBuilder.mN.contentView; - return makeMediaBigContentView(customContent); + return makeMediaExpandedContentView(customContent); } /** @@ -14491,7 +14530,7 @@ public class Notification implements Parcelable public static int VIEW_TYPE_UNSPECIFIED = 0; public static int VIEW_TYPE_NORMAL = 1; - public static int VIEW_TYPE_BIG = 2; + public static int VIEW_TYPE_EXPANDED = 2; public static int VIEW_TYPE_HEADS_UP = 3; public static int VIEW_TYPE_MINIMIZED = 4; // header only for minimized state public static int VIEW_TYPE_PUBLIC = 5; // header only for automatic public version @@ -14779,12 +14818,23 @@ public class Notification implements Parcelable } else { mBackgroundColor = rawColor; } - mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( - ContrastColorUtil.resolvePrimaryColor(ctx, mBackgroundColor, nightMode), - mBackgroundColor, 4.5); - mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( - ContrastColorUtil.resolveSecondaryColor(ctx, mBackgroundColor, nightMode), - mBackgroundColor, 4.5); + if (Flags.uiRichOngoing()) { + boolean isBgDark = Notification.Builder.isColorDark(mBackgroundColor); + int onSurfaceColorExtreme = isBgDark ? Color.WHITE : Color.BLACK; + mPrimaryTextColor = ContrastColorUtil.ensureContrast( + ColorUtils.blendARGB(mBackgroundColor, onSurfaceColorExtreme, 0.9f), + mBackgroundColor, isBgDark, 4.5); + mSecondaryTextColor = ContrastColorUtil.ensureContrast( + ColorUtils.blendARGB(mBackgroundColor, onSurfaceColorExtreme, 0.8f), + mBackgroundColor, isBgDark, 4.5); + } else { + mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( + ContrastColorUtil.resolvePrimaryColor(ctx, mBackgroundColor, nightMode), + mBackgroundColor, 4.5); + mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( + ContrastColorUtil.resolveSecondaryColor(ctx, + mBackgroundColor, nightMode), mBackgroundColor, 4.5); + } mContrastColor = mPrimaryTextColor; mPrimaryAccentColor = mPrimaryTextColor; mSecondaryAccentColor = mSecondaryTextColor; diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index e218418336c5..3973c58c0708 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -61,6 +61,8 @@ import java.util.Random; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** @@ -680,12 +682,17 @@ public class PropertyInvalidatedCache<Query, Result> { @GuardedBy("mLock") private boolean mTestMode = false; - /** - * The local value of the handler, used during testing but also used directly by the - * NonceLocal handler. - */ + // This is the local value of the nonce, as last set by the NonceHandler. It is always + // updated by the setNonce() operation. The getNonce() operation returns this value in + // NonceLocal handlers and handlers in test mode. + @GuardedBy("mLock") + protected long mShadowNonce = NONCE_UNSET; + + // A list of watchers to be notified of changes. This is null until at least one watcher + // registers. Checking for null is meant to be the fastest way the handler can determine + // that there are no watchers to be notified. @GuardedBy("mLock") - protected long mTestNonce = NONCE_UNSET; + private ArrayList<Semaphore> mWatchers; /** * The methods to get and set a nonce from whatever storage is being used. mLock may be @@ -701,27 +708,60 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Get a nonce from storage. If the handler is in test mode, the nonce is returned from - * the local mTestNonce. + * the local mShadowNonce. */ long getNonce() { synchronized (mLock) { - if (mTestMode) return mTestNonce; + if (mTestMode) return mShadowNonce; } return getNonceInternal(); } /** - * Write a nonce to storage. If the handler is in test mode, the nonce is written to the - * local mTestNonce and storage is not affected. + * Write a nonce to storage. The nonce is always written to the local mShadowNonce. If + * the handler is not in test mode the nonce is also written to storage. */ void setNonce(long val) { synchronized (mLock) { - if (mTestMode) { - mTestNonce = val; - return; + mShadowNonce = val; + if (!mTestMode) { + setNonceInternal(val); + } + wakeAllWatchersLocked(); + } + } + + @GuardedBy("mLock") + private void wakeAllWatchersLocked() { + if (mWatchers != null) { + for (int i = 0; i < mWatchers.size(); i++) { + mWatchers.get(i).release(); + } + } + } + + /** + * Register a watcher to be notified when a nonce changes. There is no check for + * duplicates. In general, this method is called only from {@link NonceWatcher}. + */ + void registerWatcher(Semaphore s) { + synchronized (mLock) { + if (mWatchers == null) { + mWatchers = new ArrayList<>(); + } + mWatchers.add(s); + } + } + + /** + * Unregister a watcher. Nothing happens if the watcher is not registered. + */ + void unregisterWatcher(Semaphore s) { + synchronized (mLock) { + if (mWatchers != null) { + mWatchers.remove(s); } } - setNonceInternal(val); } /** @@ -854,7 +894,7 @@ public class PropertyInvalidatedCache<Query, Result> { void setTestMode(boolean mode) { synchronized (mLock) { mTestMode = mode; - mTestNonce = NONCE_UNSET; + mShadowNonce = NONCE_UNSET; } } @@ -1028,7 +1068,7 @@ public class PropertyInvalidatedCache<Query, Result> { /** * SystemProperties and shared storage are protected and cannot be written by random * processes. So, for testing purposes, the NonceLocal handler stores the nonce locally. The - * NonceLocal uses the mTestNonce in the superclass, regardless of test mode. + * NonceLocal uses the mShadowNonce in the superclass, regardless of test mode. */ private static class NonceLocal extends NonceHandler { // The saved nonce. @@ -1040,16 +1080,130 @@ public class PropertyInvalidatedCache<Query, Result> { @Override long getNonceInternal() { - return mTestNonce; + return mShadowNonce; } @Override void setNonceInternal(long value) { - mTestNonce = value; + mShadowNonce = value; + } + } + + /** + * A NonceWatcher lets an external client test if a nonce value has changed from the last time + * the watcher was checked. + * @hide + */ + public static class NonceWatcher implements AutoCloseable { + // The handler for the key. + private final NonceHandler mHandler; + + // The last-seen value. This is initialized to "unset". + private long mLastSeen = NONCE_UNSET; + + // The semaphore that the watcher waits on. A permit is released every time the nonce + // changes. Permits are acquired in the wait method. + private final Semaphore mSem = new Semaphore(0); + + /** + * Create a watcher for a handler. The last-seen value is not set here and will be + * "unset". Therefore, a call to isChanged() will return true if the nonce has ever been + * set, no matter when the watcher is first created. Clients may want to flush that + * change by calling isChanged() immediately after constructing the object. + */ + private NonceWatcher(@NonNull NonceHandler handler) { + mHandler = handler; + mHandler.registerWatcher(mSem); + } + + /** + * Unregister to be notified when a nonce changes. NonceHandler allows a call to + * unregisterWatcher with a semaphore that is not registered, so there is no check inside + * this method to guard against multiple closures. + */ + @Override + public void close() { + mHandler.unregisterWatcher(mSem); + } + + /** + * Return the last seen value of the nonce. This does not update that value. Only + * {@link #isChanged()} updates the value. + */ + public long lastSeen() { + return mLastSeen; + } + + /** + * Return true if the nonce has changed from the last time isChanged() was called. The + * method is not thread safe. + * @hide + */ + public boolean isChanged() { + long current = mHandler.getNonce(); + if (current != mLastSeen) { + mLastSeen = current; + return true; + } + return false; + } + + /** + * Wait for the nonce value to change. It is not guaranteed that the nonce has changed when + * this returns: clients must confirm with {@link #isChanged}. The wait operation is only + * effective in a process that writes the nonces. The function returns the number of times + * the nonce had changed since the last call to the method. + * @hide + */ + public int waitForChange() throws InterruptedException { + mSem.acquire(1); + return 1 + mSem.drainPermits(); + } + + /** + * Wait for the nonce value to change. It is not guaranteed that the nonce has changed when + * this returns: clients must confirm with {@link #isChanged}. The wait operation is only + * effective in a process that writes the nonces. The function returns the number of times + * the nonce changed since the last call to the method. A return value of zero means the + * timeout expired. Beware that a timeout of 0 means the function will not wait at all. + * @hide + */ + public int waitForChange(long timeout, TimeUnit timeUnit) throws InterruptedException { + if (mSem.tryAcquire(1, timeout, timeUnit)) { + return 1 + mSem.drainPermits(); + } else { + return 0; + } + } + + /** + * Wake the watcher by releasing the semaphore. This can be used to wake clients that are + * blocked in {@link #waitForChange} without affecting the underlying nonce. + * @hide + */ + public void wakeUp() { + mSem.release(); } } /** + * Return a NonceWatcher for the cache. + * @hide + */ + public NonceWatcher getNonceWatcher() { + return new NonceWatcher(mNonce); + } + + /** + * Return a NonceWatcher for the given property. If a handler does not exist for the + * property, one is created. This throws if the property name is not a valid cache key. + * @hide + */ + public static NonceWatcher getNonceWatcher(@NonNull String propertyName) { + return new NonceWatcher(getNonceHandler(propertyName)); + } + + /** * Complete key prefixes. */ private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + "."; @@ -1663,6 +1817,26 @@ public class PropertyInvalidatedCache<Query, Result> { } /** + * Non-static version of corkInvalidations() for situations in which the cache instance is + * available. This is slightly faster than than the static versions because it does not have + * to look up the NonceHandler for a given property name. + * @hide + */ + public void corkInvalidations() { + mNonce.cork(); + } + + /** + * Non-static version of uncorkInvalidations() for situations in which the cache instance is + * available. This is slightly faster than than the static versions because it does not have + * to look up the NonceHandler for a given property name. + * @hide + */ + public void uncorkInvalidations() { + mNonce.uncork(); + } + + /** * Invalidate caches in all processes that are keyed for the module and api. * @hide */ diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 5ed1f4e35533..637187e01160 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -177,6 +177,10 @@ { "file_patterns": ["(/|^)AppOpsManager.java"], "name": "CtsAppOpsTestCases" + }, + { + "file_patterns": ["(/|^)BroadcastStickyCache.java"], + "name": "BroadcastUnitTests" } ] } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index abb2dd465576..a8671cf74619 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -2491,6 +2491,27 @@ public class WallpaperManager { return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); } + /** + * Version of setBitmap that allows specification of wallpaper metadata including how the + * wallpaper will be positioned for different display sizes. + * + * @param fullImage A bitmap that will supply the wallpaper imagery. + * @param description Wallpaper metadata including desired cropping + * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper + * image for restore to a future device; {@code false} otherwise. + * @param which Flags indicating which wallpaper(s) to configure with the new imagery. + * @hide + */ + @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + @TestApi + @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) + public int setBitmapWithDescription(@Nullable Bitmap fullImage, + @NonNull WallpaperDescription description, boolean allowBackup, + @SetWallpaperFlags int which) throws IOException { + return setBitmapWithCrops(fullImage, description.getCropHints(), allowBackup, which, + mContext.getUserId()); + } + private final void validateRect(Rect rect) { if (rect != null && rect.isEmpty()) { throw new IllegalArgumentException("visibleCrop rectangle must be valid and non-empty"); @@ -2700,6 +2721,27 @@ public class WallpaperManager { } /** + * Version of setStream that allows specification of wallpaper metadata including how the + * wallpaper will be positioned for different display sizes. + * + * @param bitmapData A stream containing the raw data to install as a wallpaper. This + * data can be in any format handled by {@link BitmapRegionDecoder}. + * @param description Wallpaper metadata including desired cropping + * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper + * image for restore to a future device; {@code false} otherwise. + * @param which Flags indicating which wallpaper(s) to configure with the new imagery. + * @hide + */ + @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + @TestApi + @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) + public int setStreamWithDescription(@NonNull InputStream bitmapData, + @NonNull WallpaperDescription description, boolean allowBackup, + @SetWallpaperFlags int which) throws IOException { + return setStreamWithCrops(bitmapData, description.getCropHints(), allowBackup, which); + } + + /** * Return whether any users are currently set to use the wallpaper * with the given resource ID. That is, their wallpaper has been * set through {@link #setResource(int)} with the same resource id. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 42fa9e7d7994..74d729857cc8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4177,6 +4177,35 @@ public class DevicePolicyManager { } } + + /** + * Similar to the public variant of {@link #setMtePolicy} but for use by the system. + * + * <p>Called by a system service only, meaning that the caller's UID must be equal to + * {@link Process#SYSTEM_UID}. + * + * @throws SecurityException if caller is not permitted to set Mte policy + * @throws UnsupportedOperationException if the device does not support MTE + * @param systemEntity The service entity that adds the restriction. A user restriction set by + * a service entity can only be cleared by the same entity. This can be + * just the calling package name, or any string of the caller's choice + * can be used. + * @param policy the MTE policy to be set + * @hide + */ + public void setMtePolicy(@NonNull String systemEntity, @MtePolicy int policy) { + throwIfParentInstance("setMtePolicyForUser"); + if (mService != null) { + try { + mService.setMtePolicyBySystem(systemEntity, policy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + + /** * Called by a device owner, profile owner of an organization-owned device to * get the Memory Tagging Extension (MTE) policy diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d048b5371fc4..e7be822d52d3 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -617,6 +617,7 @@ interface IDevicePolicyManager { int[] getApplicationExemptions(String packageName); void setMtePolicy(int flag, String callerPackageName); + void setMtePolicyBySystem(in String systemEntity, int policy); int getMtePolicy(String callerPackageName); void setManagedSubscriptionsPolicy(in ManagedSubscriptionsPolicy policy); diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java index 4a142bb5287a..3ee00ca3d941 100644 --- a/core/java/android/app/wallpaper/WallpaperDescription.java +++ b/core/java/android/app/wallpaper/WallpaperDescription.java @@ -19,8 +19,14 @@ package android.app.wallpaper; import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; import android.annotation.FlaggedApi; +import android.annotation.SuppressLint; +import android.annotation.TestApi; import android.app.WallpaperInfo; +import android.app.WallpaperManager; +import android.app.WallpaperManager.ScreenOrientation; import android.content.ComponentName; +import android.graphics.Point; +import android.graphics.Rect; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -29,6 +35,8 @@ import android.text.Html; import android.text.Spanned; import android.text.SpannedString; import android.util.Log; +import android.util.Pair; +import android.util.SparseArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -43,6 +51,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -71,12 +80,15 @@ public final class WallpaperDescription implements Parcelable { @Nullable private final Uri mContextUri; @Nullable private final CharSequence mContextDescription; @NonNull private final PersistableBundle mContent; + @NonNull private final SparseArray<Rect> mCropHints; + private final float mSampleSize; private WallpaperDescription(@Nullable ComponentName component, @Nullable String id, @Nullable Uri thumbnail, @Nullable CharSequence title, @Nullable List<CharSequence> description, @Nullable Uri contextUri, @Nullable CharSequence contextDescription, - @Nullable PersistableBundle content) { + @Nullable PersistableBundle content, @NonNull SparseArray<Rect> cropHints, + float sampleSize) { this.mComponent = component; this.mId = id; this.mThumbnail = thumbnail; @@ -85,6 +97,8 @@ public final class WallpaperDescription implements Parcelable { this.mContextUri = contextUri; this.mContextDescription = contextDescription; this.mContent = (content != null) ? content : new PersistableBundle(); + this.mCropHints = cropHints; + this.mSampleSize = sampleSize; } /** @return the component for this wallpaper, or {@code null} for a static wallpaper */ @@ -134,6 +148,24 @@ public final class WallpaperDescription implements Parcelable { return mContent; } + /** + * @return the cropping for the current image as described in + * {@link Builder#setCropHints(SparseArray)} + * @hide + */ + @NonNull + public SparseArray<Rect> getCropHints() { + return mCropHints; + } + + /** + * @return the subsamling size as described in {@link Builder#setSampleSize(float)}. + * @hide + */ + public float getSampleSize() { + return mSampleSize; + } + ////// Comparison overrides @Override @@ -163,9 +195,23 @@ public final class WallpaperDescription implements Parcelable { if (mContextDescription != null) { out.attribute(null, "contextdescription", toHtml(mContextDescription)); } + + for (Pair<Integer, String> pair : screenDimensionPairs()) { + @ScreenOrientation int orientation = pair.first; + String attrName = pair.second; + Rect cropHint = mCropHints.get(orientation); + if (cropHint == null) continue; + out.attributeInt(null, "cropLeft" + attrName, cropHint.left); + out.attributeInt(null, "cropTop" + attrName, cropHint.top); + out.attributeInt(null, "cropRight" + attrName, cropHint.right); + out.attributeInt(null, "cropBottom" + attrName, cropHint.bottom); + } + out.attributeFloat(null, "sampleSize", mSampleSize); + out.startTag(null, XML_TAG_DESCRIPTION); for (CharSequence s : mDescription) out.attribute(null, "descriptionline", toHtml(s)); out.endTag(null, XML_TAG_DESCRIPTION); + try { out.startTag(null, XML_TAG_CONTENT); mContent.saveToXml(out); @@ -194,6 +240,19 @@ public final class WallpaperDescription implements Parcelable { CharSequence contextDescription = fromHtml( in.getAttributeValue(null, "contextdescription")); + SparseArray<Rect> cropHints = new SparseArray<>(); + screenDimensionPairs().forEach(pair -> { + @ScreenOrientation int orientation = pair.first; + String attrName = pair.second; + Rect crop = new Rect( + in.getAttributeInt(null, "cropLeft" + attrName, 0), + in.getAttributeInt(null, "cropTop" + attrName, 0), + in.getAttributeInt(null, "cropRight" + attrName, 0), + in.getAttributeInt(null, "cropBottom" + attrName, 0)); + if (!crop.isEmpty()) cropHints.put(orientation, crop); + }); + float sampleSize = in.getAttributeFloat(null, "sampleSize", 1f); + List<CharSequence> description = new ArrayList<>(); PersistableBundle content = null; int type; @@ -213,7 +272,7 @@ public final class WallpaperDescription implements Parcelable { } return new WallpaperDescription(componentName, id, thumbnail, title, description, - contextUri, contextDescription, content); + contextUri, contextDescription, content, cropHints, sampleSize); } private static String toHtml(@NonNull CharSequence c) { @@ -253,6 +312,13 @@ public final class WallpaperDescription implements Parcelable { mContextUri = Uri.CREATOR.createFromParcel(in); mContextDescription = in.readCharSequence(); mContent = PersistableBundle.CREATOR.createFromParcel(in); + mCropHints = new SparseArray<>(); + screenDimensionPairs().forEach(pair -> { + int orientation = pair.first; + Rect crop = in.readTypedObject(Rect.CREATOR); + if (crop != null) mCropHints.put(orientation, crop); + }); + mSampleSize = in.readFloat(); } @NonNull @@ -283,6 +349,11 @@ public final class WallpaperDescription implements Parcelable { Uri.writeToParcel(dest, mContextUri); dest.writeCharSequence(mContextDescription); dest.writePersistableBundle(mContent); + screenDimensionPairs().forEach(pair -> { + int orientation = pair.first; + dest.writeTypedObject(mCropHints.get(orientation), flags); + }); + dest.writeFloat(mSampleSize); } ////// Builder @@ -293,9 +364,17 @@ public final class WallpaperDescription implements Parcelable { */ @NonNull public Builder toBuilder() { - return new Builder().setComponent(mComponent).setId(mId).setThumbnail(mThumbnail).setTitle( - mTitle).setDescription(mDescription).setContextUri( - mContextUri).setContextDescription(mContextDescription).setContent(mContent); + return new Builder() + .setComponent(mComponent) + .setId(mId) + .setThumbnail(mThumbnail) + .setTitle(mTitle) + .setDescription(mDescription) + .setContextUri(mContextUri) + .setContextDescription(mContextDescription) + .setContent(mContent) + .setCropHints(mCropHints) + .setSampleSize(mSampleSize); } /** Builder for the immutable {@link WallpaperDescription} class */ @@ -308,6 +387,9 @@ public final class WallpaperDescription implements Parcelable { @Nullable private Uri mContextUri; @Nullable private CharSequence mContextDescription; @NonNull private PersistableBundle mContent = new PersistableBundle(); + @NonNull + private SparseArray<Rect> mCropHints = new SparseArray<>(); + private float mSampleSize = 1f; /** Creates a new, empty {@link Builder}. */ public Builder() {} @@ -416,11 +498,69 @@ public final class WallpaperDescription implements Parcelable { return this; } + /** + * Defines which part of the source wallpaper image is in the stored crop file. + * + * @param cropHints map from screen dimensions to a sub-region of the image to display + * for those dimensions. The {@code Rect} sub-region may have a larger + * width/height ratio than the screen dimensions to apply a horizontal + * parallax effect. If the map is empty or some entries are missing, the + * system will apply a default strategy to position the wallpaper for + * any unspecified screen dimensions. + * @hide + */ + @NonNull + @TestApi + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setCropHints(@NonNull Map<Point, Rect> cropHints) { + mCropHints = new SparseArray<>(); + cropHints.forEach( + (point, rect) -> mCropHints.put(WallpaperManager.getOrientation(point), rect)); + return this; + } + + /** + * Defines which part of the source wallpaper image is in the stored crop file. + * + * @param cropHints map from {@link ScreenOrientation} to a sub-region of the image to + * display for that screen orientation. + * @hide + */ + @NonNull + @TestApi + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setCropHints(@NonNull SparseArray<Rect> cropHints) { + mCropHints = cropHints; + return this; + } + + /** + * How much the crop is sub-sampled. A value > 1 means that the image quality was reduced. + * This is the ratio between the cropHint height and the actual stored crop file height. + * height. + * + * @param sampleSize Sub-sampling value + * @hide + */ + @NonNull + public Builder setSampleSize(float sampleSize) { + mSampleSize = sampleSize; + return this; + } + /** Creates and returns the {@link WallpaperDescription} represented by this builder. */ @NonNull public WallpaperDescription build() { return new WallpaperDescription(mComponent, mId, mThumbnail, mTitle, mDescription, - mContextUri, mContextDescription, mContent); + mContextUri, mContextDescription, mContent, mCropHints, mSampleSize); } } + + private static List<Pair<Integer, String>> screenDimensionPairs() { + return List.of( + new Pair<>(WallpaperManager.ORIENTATION_PORTRAIT, "Portrait"), + new Pair<>(WallpaperManager.ORIENTATION_LANDSCAPE, "Landscape"), + new Pair<>(WallpaperManager.ORIENTATION_SQUARE_PORTRAIT, "SquarePortrait"), + new Pair<>(WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE, "SquareLandscape")); + } } diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 4472c3d13d7c..04668479d8e3 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -1229,7 +1229,6 @@ public final class CompanionDeviceManager { } } - // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut. /** * Register to receive callbacks whenever the associated device comes in and out of range. * @@ -1261,7 +1260,12 @@ public final class CompanionDeviceManager { * * @throws DeviceNotAssociatedException if the given device was not previously associated * with this app. + * + * @deprecated use {@link #startObservingDevicePresence(ObservingDevicePresenceRequest)} + * instead. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) + @Deprecated @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String deviceAddress) throws DeviceNotAssociatedException { @@ -1288,7 +1292,7 @@ public final class CompanionDeviceManager { callingUid, callingPid); } } - // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut. + /** * Unregister for receiving callbacks whenever the associated device comes in and out of range. * @@ -1305,7 +1309,12 @@ public final class CompanionDeviceManager { * * @throws DeviceNotAssociatedException if the given device was not previously associated * with this app. + * + * @deprecated use {@link #stopObservingDevicePresence(ObservingDevicePresenceRequest)} + * instead. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) + @Deprecated @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String deviceAddress) throws DeviceNotAssociatedException { diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java index db080fcc7702..316d129bd6b9 100644 --- a/core/java/android/companion/CompanionDeviceService.java +++ b/core/java/android/companion/CompanionDeviceService.java @@ -247,12 +247,14 @@ public abstract class CompanionDeviceService extends Service { .detachSystemDataTransport(associationId); } - // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut. /** * Called by the system when an associated device is nearby or connected. * * @param associationInfo A record for the companion device. + * @deprecated use {@link #onDevicePresenceEvent(DevicePresenceEvent)}} instead. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) + @Deprecated @MainThread public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) { if (!associationInfo.isSelfManaged()) { @@ -260,12 +262,14 @@ public abstract class CompanionDeviceService extends Service { } } - // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut. /** * Called by the system when an associated device is out of range or disconnected. * * @param associationInfo A record for the companion device. + * @deprecated use {@link #onDevicePresenceEvent(DevicePresenceEvent)}} instead. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) + @Deprecated @MainThread public void onDeviceDisappeared(@NonNull AssociationInfo associationInfo) { if (!associationInfo.isSelfManaged()) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index acad92c90c59..6e2ca2c109f1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4250,6 +4250,7 @@ public abstract class Context { //@hide: WIFI_RTT_SERVICE, //@hide: ETHERNET_SERVICE, WIFI_RTT_RANGING_SERVICE, + WIFI_USD_SERVICE, NSD_SERVICE, AUDIO_SERVICE, AUDIO_DEVICE_VOLUME_SERVICE, @@ -5096,6 +5097,19 @@ public abstract class Context { */ public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.usd.UsdManager} for Unsynchronized Service Discovery (USD) operation. + * + * @see #getSystemService(String) + * @see android.net.wifi.usd.UsdManager + * @hide + */ + @FlaggedApi(android.net.wifi.flags.Flags.FLAG_USD) + @SystemApi + public static final String WIFI_USD_SERVICE = "wifi_usd"; + /** * Use with {@link #getSystemService(String)} to retrieve a {@link * android.net.lowpan.LowpanManager} for handling management of diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3d2d487b9d06..02eed1a7553f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7720,6 +7720,7 @@ public class Intent implements Parcelable, Cloneable { @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = { EXTENDED_FLAG_FILTER_MISMATCH, EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN, + EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED, }) @Retention(RetentionPolicy.SOURCE) public @interface ExtendedFlags {} @@ -7740,6 +7741,13 @@ public class Intent implements Parcelable, Cloneable { */ public static final int EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN = 1 << 1; + /** + * This flag indicates this intent called {@link #collectExtraIntentKeys()}. + * + * @hide + */ + public static final int EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED = 1 << 2; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // toUri() and parseUri() options. @@ -12328,7 +12336,8 @@ public class Intent implements Parcelable, Cloneable { } private void collectNestedIntentKeysRecur(Set<Intent> visited) { - if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) { + addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED); + if (mExtras != null && !mExtras.isEmpty()) { for (String key : mExtras.keySet()) { Object value = mExtras.get(key); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index cccfdb0938e5..94784227049d 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1449,6 +1449,97 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } } + /** + * Use this to report any errors during alignment checks + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_ERROR = -1; + + /** + * Initial value for mPageSizeAppCompatFlags + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED = 0; + + /** + * if set, extract libs forcefully for 16 KB device and show warning dialog. + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED = 1 << 1; + + /** + * if set, load 4 KB aligned ELFs on 16 KB device in compat mode and show warning dialog. + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED = 1 << 2; + + /** + * Run in 16 KB app compat mode. This flag will be set explicitly through settings. If set, 16 + * KB app compat warning dialogs will still show up. + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED = 1 << 3; + + /** + * Disable 16 KB app compat mode through settings. It should only affect ELF loading as app is + * already installed. + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED = 1 << 4; + + /** + * Run in 16 KB app compat mode. This flag will be set explicitly through manifest. If set, hide + * the 16 KB app compat warning dialogs. This has the highest priority to enable compat mode. + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED = 1 << 5; + + /** + * Disable 16 KB app compat mode. This has the highest priority to disable compat mode. + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED = 1 << 6; + + /** + * Max value for page size app compat + * + * @hide + */ + public static final int PAGE_SIZE_APP_COMPAT_FLAG_MAX = 1 << 7; + + /** + * 16 KB app compat status for the app. App can have native shared libs which are not page + * aligned, LOAD segments inside the shared libs have to be page aligned. Apps can specify the + * override in manifest file as well. + */ + private @PageSizeAppCompatFlags int mPageSizeAppCompatFlags = + ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED; + + /** {@hide} */ + @IntDef( + prefix = {"PAGE_SIZE_APP_COMPAT_FLAG_"}, + value = { + PAGE_SIZE_APP_COMPAT_FLAG_ERROR, + PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED, + PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED, + PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED, + PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED, + PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED, + PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED, + PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED, + PAGE_SIZE_APP_COMPAT_FLAG_MAX, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PageSizeAppCompatFlags {} + /** @hide */ public String classLoaderName; @@ -1777,7 +1868,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "enableOnBackInvokedCallback=" + isOnBackInvokedCallbackEnabled()); pw.println(prefix + "allowCrossUidActivitySwitchFromBelow=" + allowCrossUidActivitySwitchFromBelow); - + pw.println(prefix + "mPageSizeAppCompatFlags=" + mPageSizeAppCompatFlags); } pw.println(prefix + "createTimestamp=" + createTimestamp); if (mKnownActivityEmbeddingCerts != null) { @@ -1897,6 +1988,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } proto.write(ApplicationInfoProto.Detail.ALLOW_CROSS_UID_ACTIVITY_SWITCH_FROM_BELOW, allowCrossUidActivitySwitchFromBelow); + + proto.write(ApplicationInfoProto.Detail.ENABLE_PAGE_SIZE_APP_COMPAT, + mPageSizeAppCompatFlags); + proto.end(detailToken); } if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) { @@ -2024,6 +2119,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { localeConfigRes = orig.localeConfigRes; allowCrossUidActivitySwitchFromBelow = orig.allowCrossUidActivitySwitchFromBelow; createTimestamp = SystemClock.uptimeMillis(); + mPageSizeAppCompatFlags = orig.mPageSizeAppCompatFlags; } public String toString() { @@ -2128,6 +2224,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } dest.writeInt(localeConfigRes); dest.writeInt(allowCrossUidActivitySwitchFromBelow ? 1 : 0); + dest.writeInt(mPageSizeAppCompatFlags); sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags); } @@ -2228,6 +2325,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } localeConfigRes = source.readInt(); allowCrossUidActivitySwitchFromBelow = source.readInt() != 0; + mPageSizeAppCompatFlags = source.readInt(); mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source); if (mKnownActivityEmbeddingCerts.isEmpty()) { @@ -2765,6 +2863,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { requestRawExternalStorageAccess = value; } + /** {@hide} */ + public void setPageSizeAppCompatFlags(@PageSizeAppCompatFlags int value) { + mPageSizeAppCompatFlags |= value; + } + /** * Replaces {@link #mAppClassNamesByProcess}. This takes over the ownership of the passed map. * Do not modify the argument at the callsite. diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 5d4babb8a36d..9f898b823a76 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -848,4 +848,12 @@ interface IPackageManager { int getAppMetadataSource(String packageName, int userId); ComponentName getDomainVerificationAgent(int userId); + + void setPageSizeAppCompatFlagsSettingsOverride(in String packageName, boolean enabled); + + boolean isPageSizeCompatEnabled(in String packageName); + + String getPageSizeCompatWarningMessage(in String packageName); + + List<String> getAllApexDirectories(); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index d2b43b9bd2b4..23d3693628e7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -804,7 +804,6 @@ public abstract class PackageManager { @Deprecated private void __metadata() {} - //@formatter:on // End of generated code @@ -6729,6 +6728,11 @@ public abstract class PackageManager { * If the given permission already exists, the info you supply here * will be used to update it. * + * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic + * permissions should declare their permissions statically inside their app manifest instead. + * This method will become a no-op in a future Android release and eventually be removed from + * the SDK. + * * @param info Description of the permission to be added. * * @return Returns true if a new permission was created, false if an @@ -6739,7 +6743,9 @@ public abstract class PackageManager { * * @see #removePermission(String) */ - //@Deprecated + @SuppressWarnings("HiddenAbstractMethod") + @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED) + @Deprecated public abstract boolean addPermission(@NonNull PermissionInfo info); /** @@ -6748,8 +6754,15 @@ public abstract class PackageManager { * allowing it to return quicker and batch a series of adds at the * expense of no guarantee the added permission will be retained if * the device is rebooted before it is written. + * + * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic + * permissions should declare their permissions statically inside their app manifest instead. + * This method will become a no-op in a future Android release and eventually be removed from + * the SDK. */ - //@Deprecated + @SuppressWarnings("HiddenAbstractMethod") + @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED) + @Deprecated public abstract boolean addPermissionAsync(@NonNull PermissionInfo info); /** @@ -6758,6 +6771,11 @@ public abstract class PackageManager { * -- you are only allowed to remove permissions that you are allowed * to add. * + * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic + * permissions should declare their permissions statically inside their app manifest instead. + * This method will become a no-op in a future Android release and eventually be removed from + * the SDK. + * * @param permName The name of the permission to remove. * * @throws SecurityException if you are not allowed to remove the @@ -6765,7 +6783,9 @@ public abstract class PackageManager { * * @see #addPermission(PermissionInfo) */ - //@Deprecated + @SuppressWarnings("HiddenAbstractMethod") + @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED) + @Deprecated public abstract void removePermission(@NonNull String permName); /** @@ -10987,6 +11007,41 @@ public abstract class PackageManager { } /** + * Set the page compat mode override for given package + * + * @hide + */ + @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) + public void setPageSizeAppCompatFlagsSettingsOverride(@NonNull String packageName, + boolean enabled) { + throw new UnsupportedOperationException( + "setPageSizeAppCompatFlagsSettingsOverride not implemented in subclass"); + } + + /** + * Check whether page size app compat mode is enabled for given package + * + * @hide + */ + @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) + public boolean isPageSizeCompatEnabled(@NonNull String packageName) { + throw new UnsupportedOperationException( + "isPageSizeCompatEnabled not implemented in subclass"); + } + + /** + * Get the page size app compat warning dialog to show at app launch time + * + * @hide + */ + @Nullable + @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) + public String getPageSizeCompatWarningMessage(@NonNull String packageName) { + throw new UnsupportedOperationException( + "getPageSizeCompatWarningMessage not implemented in subclass"); + } + + /** * Returns the harmful app warning string for the given app, or null if there is none set. * * @param packageName The full name of the desired package. diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 813208d7ff38..833260a15c45 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -341,6 +341,28 @@ flag { is_fixed_read_only: true } +flag { + name: "cache_user_start_realtime_read_only" + namespace: "multiuser" + description: "Cache getUserStartRealtime to avoid unnecessary binder calls" + bug: "350416205" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} + +flag { + name: "cache_user_unlock_realtime_read_only" + namespace: "multiuser" + description: "Cache getUserUnlockRealtime to avoid unnecessary binder calls" + bug: "350421407" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} + # This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile. flag { name: "enable_private_space_features" diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index 153dd9a93490..e30f871b68eb 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -330,6 +330,36 @@ public class FrameworkParsingPackageUtils { } /** + * Check if a package is compatible with this platform with regards to its + * its minSdkVersionFull. + * + * @param minSdkVersionFullString A string representation of a major.minor version, + * e.g. "12.34" + * @param platformMinSdkVersionFull The major and minor version of the platform, i.e. the value + * of Build.VERSION.SDK_INT_FULL + * @param input A ParseInput object to report success or failure + */ + public static ParseResult<Void> verifyMinSdkVersionFull(@NonNull String minSdkVersionFullString, + int platformMinSdkVersionFull, @NonNull ParseInput input) { + int minSdkVersionFull; + try { + minSdkVersionFull = Build.parseFullVersion(minSdkVersionFullString); + } catch (IllegalStateException e) { + return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + e.getMessage()); + } + if (minSdkVersionFull <= platformMinSdkVersionFull) { + return input.success(null); + } + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, + "Requires newer sdk version " + + Build.fullVersionToString(minSdkVersionFull) + + " (current version is " + + Build.fullVersionToString(platformMinSdkVersionFull) + + ")"); + } + + /** * Computes the targetSdkVersion to use at runtime. If the package is not compatible with this * platform, populates {@code outError[0]} with an error message. * <p> diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java index 7e42f43056e1..29eaab8eebcc 100644 --- a/core/java/android/hardware/camera2/TotalCaptureResult.java +++ b/core/java/android/hardware/camera2/TotalCaptureResult.java @@ -86,7 +86,8 @@ public final class TotalCaptureResult extends CaptureResult { mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>(); for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) { TotalCaptureResult physicalResult = new TotalCaptureResult( - onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(), + onePhysicalResult.getCameraId(), + onePhysicalResult.getCameraMetadata(), parent, extras, /*partials*/null, sessionId, new PhysicalCaptureResultInfo[0]); mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(), physicalResult); @@ -115,7 +116,8 @@ public final class TotalCaptureResult extends CaptureResult { mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>(); for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) { TotalCaptureResult physicalResult = new TotalCaptureResult( - onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(), + onePhysicalResult.getCameraId(), + onePhysicalResult.getCameraMetadata(), parent, requestId, frameNumber, /*partials*/null, sessionId, new PhysicalCaptureResultInfo[0]); mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(), diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index ea70abb55b48..34c0f7b19da9 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -34,6 +34,7 @@ import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.CameraMetadataInfo; import android.hardware.camera2.CameraOfflineSession; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; @@ -59,6 +60,8 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; @@ -129,6 +132,8 @@ public class CameraDeviceImpl extends CameraDevice final Object mInterfaceLock = new Object(); // access from this class and Session only! private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); + private long mFMQReader; // native fmq reader ptr + private final StateCallback mDeviceCallback; private volatile StateCallbackKK mSessionStateCallback; private final Executor mDeviceExecutor; @@ -476,6 +481,9 @@ public class CameraDeviceImpl extends CameraDevice if (mInError) return; mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice); + Parcel resultParcel = Parcel.obtain(); + mRemoteDevice.getCaptureResultMetadataQueue().writeToParcel(resultParcel, 0); + mFMQReader = nativeCreateFMQReader(resultParcel); IBinder remoteDeviceBinder = remoteDevice.asBinder(); // For legacy camera device, remoteDevice is in the same process, and @@ -1682,6 +1690,7 @@ public class CameraDeviceImpl extends CameraDevice if (mRemoteDevice != null || mInError) { mDeviceExecutor.execute(mCallOnClosed); } + nativeClose(mFMQReader); mRemoteDevice = null; } @@ -2416,27 +2425,61 @@ public class CameraDeviceImpl extends CameraDevice } } } + private PhysicalCaptureResultInfo[] readMetadata( + PhysicalCaptureResultInfo[] srcPhysicalResults) { + PhysicalCaptureResultInfo[] retVal = + new PhysicalCaptureResultInfo[srcPhysicalResults.length]; + int i = 0; + long fmqSize = 0; + for (PhysicalCaptureResultInfo srcPhysicalResult : srcPhysicalResults) { + CameraMetadataNative physicalCameraMetadata = null; + if (srcPhysicalResult.getCameraMetadataInfo().getTag() == + CameraMetadataInfo.fmqSize) { + fmqSize = srcPhysicalResult.getCameraMetadataInfo().getFmqSize(); + physicalCameraMetadata = + new CameraMetadataNative(nativeReadResultMetadata(mFMQReader, fmqSize)); + } else { + physicalCameraMetadata = srcPhysicalResult.getCameraMetadata(); + } + PhysicalCaptureResultInfo physicalResultInfo = + new PhysicalCaptureResultInfo( + srcPhysicalResult.getCameraId(), physicalCameraMetadata); + retVal[i] = physicalResultInfo; + i++; + } + return retVal; + } @Override - public void onResultReceived(CameraMetadataNative result, + public void onResultReceived(CameraMetadataInfo resultInfo, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) throws RemoteException { int requestId = resultExtras.getRequestId(); long frameNumber = resultExtras.getFrameNumber(); - - if (DEBUG) { - Log.v(TAG, "Received result frame " + frameNumber + " for id " - + requestId); - } - synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed - + PhysicalCaptureResultInfo savedPhysicalResults[] = physicalResults; + CameraMetadataNative result; + if (resultInfo.getTag() == CameraMetadataInfo.fmqSize) { + CameraMetadataNative fmqMetadata = + new CameraMetadataNative( + nativeReadResultMetadata(mFMQReader, resultInfo.getFmqSize())); + result = fmqMetadata; + } else { + result = resultInfo.getMetadata(); + } + physicalResults = readMetadata(savedPhysicalResults); + if (DEBUG) { + Log.v(TAG, "Received result frame " + frameNumber + " for id " + + requestId); + } // Redirect device callback to the offline session in case we are in the middle // of an offline switch if (mOfflineSessionImpl != null) { - mOfflineSessionImpl.getCallbacks().onResultReceived(result, resultExtras, + CameraMetadataInfo resultInfoOffline = CameraMetadataInfo.metadata(result); + mOfflineSessionImpl.getCallbacks().onResultReceived(resultInfoOffline, + resultExtras, physicalResults); return; } @@ -2824,6 +2867,11 @@ public class CameraDeviceImpl extends CameraDevice } } + private static native long nativeCreateFMQReader(Parcel resultQueue); + //TODO: Investigate adding FastNative b/62791857 + private static native long nativeReadResultMetadata(long ptr, long metadataSize); + private static native void nativeClose(long ptr); + @Override public @CAMERA_AUDIO_RESTRICTION int getCameraAudioRestriction() throws CameraAccessException { synchronized(mInterfaceLock) { @@ -2870,4 +2918,4 @@ public class CameraDeviceImpl extends CameraDevice } } } -} +}
\ No newline at end of file diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index c0a5928a369b..d7b6f116e452 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -391,6 +391,18 @@ public class CameraMetadataNative implements Parcelable { } /** + * Take ownership of native metadata + */ + public CameraMetadataNative(long metadataPtr) { + super(); + mMetadataPtr = metadataPtr; + if (mMetadataPtr == 0) { + throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); + } + updateNativeAllocation(); + } + + /** * Move the contents from {@code other} into a new camera metadata instance.</p> * * <p>After this call, {@code other} will become empty.</p> diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java index 1769c4638805..e660d6aba854 100644 --- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java @@ -28,6 +28,7 @@ import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallbac import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.CameraMetadataInfo; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraOfflineSession; import android.hardware.camera2.TotalCaptureResult; @@ -291,10 +292,10 @@ public class CameraOfflineSessionImpl extends CameraOfflineSession } @Override - public void onResultReceived(CameraMetadataNative result, + public void onResultReceived(CameraMetadataInfo resultInfo, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) throws RemoteException { - + CameraMetadataNative result = resultInfo.getMetadata(); int requestId = resultExtras.getRequestId(); long frameNumber = resultExtras.getFrameNumber(); diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index 831c75ec5d33..a79e084b7f41 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -26,6 +26,7 @@ import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.utils.ExceptionUtils; import android.hardware.camera2.utils.SubmitInfo; +import android.hardware.common.fmq.MQDescriptor; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -313,4 +314,15 @@ public class ICameraDeviceUserWrapper { throw ExceptionUtils.throwAsPublicException(e); } } -} + + public MQDescriptor<Byte, Byte> getCaptureResultMetadataQueue() throws CameraAccessException { + try { + return mRemoteDevice.getCaptureResultMetadataQueue(); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); + } + } + +}
\ No newline at end of file diff --git a/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java b/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java index 09619d0f5c7e..77296d1c1877 100644 --- a/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java +++ b/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java @@ -15,6 +15,7 @@ */ package android.hardware.camera2.impl; +import android.hardware.camera2.CameraMetadataInfo; import android.hardware.camera2.impl.CameraMetadataNative; import android.os.Parcel; @@ -25,7 +26,7 @@ import android.os.Parcelable; */ public class PhysicalCaptureResultInfo implements Parcelable { private String cameraId; - private CameraMetadataNative cameraMetadata; + private CameraMetadataInfo cameraMetadataInfo; public static final @android.annotation.NonNull Parcelable.Creator<PhysicalCaptureResultInfo> CREATOR = new Parcelable.Creator<PhysicalCaptureResultInfo>() { @@ -46,7 +47,7 @@ public class PhysicalCaptureResultInfo implements Parcelable { public PhysicalCaptureResultInfo(String cameraId, CameraMetadataNative cameraMetadata) { this.cameraId = cameraId; - this.cameraMetadata = cameraMetadata; + this.cameraMetadataInfo = CameraMetadataInfo.metadata(cameraMetadata); } @Override @@ -57,13 +58,12 @@ public class PhysicalCaptureResultInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(cameraId); - cameraMetadata.writeToParcel(dest, flags); + cameraMetadataInfo.writeToParcel(dest, flags); } public void readFromParcel(Parcel in) { cameraId = in.readString(); - cameraMetadata = new CameraMetadataNative(); - cameraMetadata.readFromParcel(in); + cameraMetadataInfo = CameraMetadataInfo.CREATOR.createFromParcel(in); } public String getCameraId() { @@ -71,6 +71,11 @@ public class PhysicalCaptureResultInfo implements Parcelable { } public CameraMetadataNative getCameraMetadata() { - return cameraMetadata; + return cameraMetadataInfo.getMetadata(); } -} + + public CameraMetadataInfo getCameraMetadataInfo() { + return cameraMetadataInfo; + } + +}
\ No newline at end of file diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java index 078b4d4629e0..7efdd6dbdf41 100644 --- a/core/java/android/hardware/contexthub/HubEndpoint.java +++ b/core/java/android/hardware/contexthub/HubEndpoint.java @@ -18,6 +18,7 @@ package android.hardware.contexthub; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -31,6 +32,8 @@ import android.util.SparseArray; import androidx.annotation.GuardedBy; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -48,6 +51,46 @@ import java.util.concurrent.Executor; public class HubEndpoint { private static final String TAG = "HubEndpoint"; + /** + * Constants describing the outcome of operations through HubEndpoints (like opening/closing of + * sessions or stopping of endpoints). + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"REASON_"}, + value = { + REASON_FAILURE, + REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED, + REASON_CLOSE_ENDPOINT_SESSION_REQUESTED, + REASON_ENDPOINT_INVALID, + REASON_ENDPOINT_STOPPED, + }) + public @interface Reason {} + + /** Unclassified failure */ + public static final int REASON_FAILURE = 0; + + // The values 1 and 2 are reserved at the Context Hub HAL but not exposed to apps. + + /** The peer rejected the request to open this endpoint session. */ + public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; + + /** The peer closed this endpoint session. */ + public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; + + /** The peer endpoint is invalid. */ + public static final int REASON_ENDPOINT_INVALID = 5; + + /** + * The endpoint is now stopped. The app should retrieve the endpoint info using {@link + * android.hardware.location.ContextHubManager#findEndpoints} or register updates through + * {@link android.hardware.location.ContextHubManager#registerEndpointDiscoveryCallback} + * to get notified if the endpoint restarts. + */ + public static final int REASON_ENDPOINT_STOPPED = 6; + private final Object mLock = new Object(); private final HubEndpointInfo mPendingHubEndpointInfo; @Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback; @@ -173,9 +216,7 @@ public class HubEndpoint { try { mServiceToken.closeSession( - sessionId, - IHubEndpointLifecycleCallback - .REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED); + sessionId, REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED); } catch (RemoteException e) { e.rethrowFromSystemServer(); } @@ -396,9 +437,7 @@ public class HubEndpoint { try { // Oneway notification to system service - serviceToken.closeSession( - session.getId(), - IHubEndpointLifecycleCallback.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED); + serviceToken.closeSession(session.getId(), REASON_CLOSE_ENDPOINT_SESSION_REQUESTED); } catch (RemoteException e) { Log.e(TAG, "closeSession: failed to close session " + session, e); e.rethrowFromSystemServer(); diff --git a/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl b/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl index 85775c05bad9..245be930a897 100644 --- a/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl +++ b/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl @@ -31,6 +31,7 @@ oneway interface IContextHubEndpointDiscoveryCallback { /** * Called when endpoint(s) stopped. * @param hubEndpointInfoList The list of endpoints that started. + * @param reason The reason why the endpoints stopped. */ - void onEndpointsStopped(in HubEndpointInfo[] hubEndpointInfoList); + void onEndpointsStopped(in HubEndpointInfo[] hubEndpointInfoList, int reason); } diff --git a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java b/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java index 0b77ddb8cb0b..a61a7ebd0de9 100644 --- a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java +++ b/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java @@ -42,7 +42,8 @@ public interface IHubEndpointDiscoveryCallback { * Called when a list of hub endpoints have stopped. * * @param discoveryInfoList The list containing hub discovery information. + * @param reason The reason the endpoints stopped. */ - // TODO(b/375487784): Add endpoint stop reason - void onEndpointsStopped(@NonNull List<HubDiscoveryInfo> discoveryInfoList); + void onEndpointsStopped( + @NonNull List<HubDiscoveryInfo> discoveryInfoList, @HubEndpoint.Reason int reason); } diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java index 46884393b49b..fe449bb5ce0e 100644 --- a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java +++ b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java @@ -17,15 +17,11 @@ package android.hardware.contexthub; import android.annotation.FlaggedApi; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.chre.flags.Flags; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * Interface for listening to lifecycle events of a hub endpoint. * @@ -34,24 +30,6 @@ import java.lang.annotation.RetentionPolicy; @SystemApi @FlaggedApi(Flags.FLAG_OFFLOAD_API) public interface IHubEndpointLifecycleCallback { - /** Unknown reason. */ - int REASON_UNSPECIFIED = 0; - - /** The peer rejected the request to open this endpoint session. */ - int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; - - /** The peer closed this endpoint session. */ - int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - REASON_UNSPECIFIED, - REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED, - REASON_CLOSE_ENDPOINT_SESSION_REQUESTED, - }) - @interface EndpointLifecycleReason {} - /** * Called when an endpoint is requesting a session be opened with another endpoint. * @@ -78,5 +56,5 @@ public interface IHubEndpointLifecycleCallback { * used. * @param reason The reason why this session was closed. */ - void onSessionClosed(@NonNull HubEndpointSession session, @EndpointLifecycleReason int reason); + void onSessionClosed(@NonNull HubEndpointSession session, @HubEndpoint.Reason int reason); } diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 5e8a18724cdf..117d8fe24809 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -791,7 +791,7 @@ public final class ContextHubManager { } @Override - public void onEndpointsStopped(HubEndpointInfo[] hubEndpointInfoList) { + public void onEndpointsStopped(HubEndpointInfo[] hubEndpointInfoList, int reason) { if (hubEndpointInfoList.length == 0) { Log.w(TAG, "onEndpointsStopped: received empty discovery list"); return; @@ -815,7 +815,7 @@ public final class ContextHubManager { if (discoveryList.isEmpty()) { Log.w(TAG, "onEndpointsStopped: no matching service descriptor"); } else { - callback.onEndpointsStopped(discoveryList); + callback.onEndpointsStopped(discoveryList, reason); } }); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 4bde8e2b44ee..5f3c15d1842e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1199,6 +1199,11 @@ public class InputMethodService extends AbstractInputMethodService { // when the stylus is not down. mPrivOps.setHandwritingSurfaceNotTouchable(true); break; + case MotionEvent.ACTION_OUTSIDE: + // TODO(b/350047836): determine if there is use-case for simultaneous touch + // and stylus handwriting and we shouldn't finish for that. + finishStylusHandwriting(); + break; } } @@ -3207,6 +3212,7 @@ public class InputMethodService extends AbstractInputMethodService { Log.d(TAG, "Setting new handwriting region for stylus handwriting " + handwritingRegion + " from last " + mLastHandwritingRegion); } + mPrivOps.setHandwritingTouchableRegion(handwritingRegion); mLastHandwritingRegion = handwritingRegion; } diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java index f0e12ca644dd..f5fee4f2d9be 100644 --- a/core/java/android/os/AggregateBatteryConsumer.java +++ b/core/java/android/os/AggregateBatteryConsumer.java @@ -17,7 +17,6 @@ package android.os; import android.annotation.NonNull; -import android.util.proto.ProtoOutputStream; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -100,19 +99,6 @@ public final class AggregateBatteryConsumer extends BatteryConsumer { } } - void writePowerComponentModelProto(@NonNull ProtoOutputStream proto) { - for (int i = 0; i < POWER_COMPONENT_COUNT; i++) { - final int powerModel = getPowerModel(i); - if (powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) continue; - - final long token = proto.start(BatteryUsageStatsAtomsProto.COMPONENT_MODELS); - proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.COMPONENT, i); - proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_MODEL, - powerModelToProtoEnum(powerModel)); - proto.end(token); - } - } - /** * Builder for DeviceBatteryConsumer. */ diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 14b67f64b6da..96ea1683b5cf 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -161,18 +161,27 @@ public abstract class BatteryConsumer { /** * Unspecified power model. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public static final int POWER_MODEL_UNDEFINED = 0; /** * Power model that is based on average consumption rates that hardware components * consume in various states. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public static final int POWER_MODEL_POWER_PROFILE = 1; /** * Power model that is based on energy consumption stats provided by PowerStats HAL. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public static final int POWER_MODEL_ENERGY_CONSUMPTION = 2; /** @@ -380,19 +389,17 @@ public abstract class BatteryConsumer { public final @ScreenState int screenState; public final @PowerState int powerState; - final int mPowerModelColumnIndex; final int mPowerColumnIndex; final int mDurationColumnIndex; private Key(@PowerComponentId int powerComponentId, @ProcessState int processState, - @ScreenState int screenState, @PowerState int powerState, int powerModelColumnIndex, + @ScreenState int screenState, @PowerState int powerState, int powerColumnIndex, int durationColumnIndex) { this.powerComponentId = powerComponentId; this.processState = processState; this.screenState = screenState; this.powerState = powerState; - mPowerModelColumnIndex = powerModelColumnIndex; mPowerColumnIndex = powerColumnIndex; mDurationColumnIndex = durationColumnIndex; } @@ -577,11 +584,11 @@ public abstract class BatteryConsumer { * * @param componentId The ID of the power component, e.g. * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + * @deprecated PowerModel is no longer supported */ + @Deprecated public @PowerModel int getPowerModel(@PowerComponentId int componentId) { - return mPowerComponents.getPowerModel( - mData.layout.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED, - SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED)); + return POWER_MODEL_UNDEFINED; } /** @@ -589,9 +596,11 @@ public abstract class BatteryConsumer { * * @param key The key of the power component, obtained by calling {@link #getKey} or * {@link #getKeys} method. + * @deprecated PowerModel is no longer supported */ + @Deprecated public @PowerModel int getPowerModel(@NonNull BatteryConsumer.Key key) { - return mPowerComponents.getPowerModel(key); + return POWER_MODEL_UNDEFINED; } /** @@ -657,20 +666,6 @@ public abstract class BatteryConsumer { } /** - * Returns the name of the specified power model. Intended for logging and debugging. - */ - public static String powerModelToString(@BatteryConsumer.PowerModel int powerModel) { - switch (powerModel) { - case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION: - return "energy consumption"; - case BatteryConsumer.POWER_MODEL_POWER_PROFILE: - return "power profile"; - default: - return ""; - } - } - - /** * Returns the equivalent PowerModel enum for the specified power model. * {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel} */ @@ -857,10 +852,8 @@ public abstract class BatteryConsumer { static class BatteryConsumerDataLayout { private static final Key[] KEY_ARRAY = new Key[0]; - public static final int POWER_MODEL_NOT_INCLUDED = -1; public final String[] customPowerComponentNames; public final int customPowerComponentCount; - public final boolean powerModelsIncluded; public final boolean processStateDataIncluded; public final boolean screenStateDataIncluded; public final boolean powerStateDataIncluded; @@ -872,11 +865,10 @@ public abstract class BatteryConsumer { private SparseArray<Key[]> mPerComponentKeys; private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames, - boolean powerModelsIncluded, boolean includeProcessStateData, - boolean includeScreenState, boolean includePowerState) { + boolean includeProcessStateData, boolean includeScreenState, + boolean includePowerState) { this.customPowerComponentNames = customPowerComponentNames; this.customPowerComponentCount = customPowerComponentNames.length; - this.powerModelsIncluded = powerModelsIncluded; this.processStateDataIncluded = includeProcessStateData; this.screenStateDataIncluded = includeScreenState; this.powerStateDataIncluded = includePowerState; @@ -904,7 +896,7 @@ public abstract class BatteryConsumer { continue; } for (int i = 0; i < powerComponentIds.length; i++) { - columnIndex = addKeys(keyList, powerModelsIncluded, includeProcessStateData, + columnIndex = addKeys(keyList, includeProcessStateData, powerComponentIds[i], screenState, powerState, columnIndex); } } @@ -934,13 +926,10 @@ public abstract class BatteryConsumer { } } - private int addKeys(List<Key> keys, boolean powerModelsIncluded, - boolean includeProcessStateData, @PowerComponentId int componentId, - int screenState, int powerState, int columnIndex) { + private int addKeys(List<Key> keys, boolean includeProcessStateData, + @PowerComponentId int componentId, int screenState, int powerState, + int columnIndex) { keys.add(new Key(componentId, PROCESS_STATE_UNSPECIFIED, screenState, powerState, - powerModelsIncluded - ? columnIndex++ - : POWER_MODEL_NOT_INCLUDED, // power model columnIndex++, // power columnIndex++ // usage duration )); @@ -956,9 +945,6 @@ public abstract class BatteryConsumer { continue; } keys.add(new Key(componentId, processState, screenState, powerState, - powerModelsIncluded - ? columnIndex++ - : POWER_MODEL_NOT_INCLUDED, // power model columnIndex++, // power columnIndex++ // usage duration )); @@ -1016,7 +1002,7 @@ public abstract class BatteryConsumer { } static BatteryConsumerDataLayout createBatteryConsumerDataLayout( - String[] customPowerComponentNames, boolean includePowerModels, + String[] customPowerComponentNames, boolean includeProcessStateData, boolean includeScreenStateData, boolean includePowerStateData) { int columnCount = BatteryConsumer.COLUMN_COUNT; @@ -1025,8 +1011,7 @@ public abstract class BatteryConsumer { columnCount = Math.max(columnCount, UserBatteryConsumer.COLUMN_COUNT); return new BatteryConsumerDataLayout(columnCount, customPowerComponentNames, - includePowerModels, includeProcessStateData, includeScreenStateData, - includePowerStateData); + includeProcessStateData, includeScreenStateData, includePowerStateData); } protected abstract static class BaseBuilder<T extends BaseBuilder<?>> { @@ -1086,7 +1071,7 @@ public abstract class BatteryConsumer { public T setConsumedPower(@PowerComponentId int componentId, double componentPower, @PowerModel int powerModel) { mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), - componentPower, powerModel); + componentPower); return (T) this; } @@ -1095,14 +1080,14 @@ public abstract class BatteryConsumer { public T addConsumedPower(@PowerComponentId int componentId, double componentPower, @PowerModel int powerModel) { mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), - componentPower, powerModel); + componentPower); return (T) this; } @SuppressWarnings("unchecked") @NonNull public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { - mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel); + mPowerComponentsBuilder.setConsumedPower(key, componentPower); return (T) this; } @@ -1110,21 +1095,14 @@ public abstract class BatteryConsumer { @NonNull public T addConsumedPower(@PowerComponentId int componentId, double componentPower) { mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), - componentPower, POWER_MODEL_UNDEFINED); + componentPower); return (T) this; } @SuppressWarnings("unchecked") @NonNull public T addConsumedPower(Key key, double componentPower) { - mPowerComponentsBuilder.addConsumedPower(key, componentPower, POWER_MODEL_UNDEFINED); - return (T) this; - } - - @SuppressWarnings("unchecked") - @NonNull - public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { - mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel); + mPowerComponentsBuilder.addConsumedPower(key, componentPower); return (T) this; } diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 72e4cef2f6eb..f913fcfd56d4 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -114,7 +114,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { static final String XML_ATTR_POWER_STATE = "power_state"; static final String XML_ATTR_POWER = "power"; static final String XML_ATTR_DURATION = "duration"; - static final String XML_ATTR_MODEL = "model"; static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity"; static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct"; static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower"; @@ -155,7 +154,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private final long mBatteryTimeRemainingMs; private final long mChargeTimeRemainingMs; private final String[] mCustomPowerComponentNames; - private final boolean mIncludesPowerModels; private final boolean mIncludesProcessStateData; private final boolean mIncludesScreenStateData; private final boolean mIncludesPowerStateData; @@ -179,7 +177,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs; mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs; mCustomPowerComponentNames = builder.mCustomPowerComponentNames; - mIncludesPowerModels = builder.mIncludePowerModels; mIncludesProcessStateData = builder.mIncludesProcessStateData; mIncludesScreenStateData = builder.mIncludesScreenStateData; mIncludesPowerStateData = builder.mIncludesPowerStateData; @@ -364,14 +361,13 @@ public final class BatteryUsageStats implements Parcelable, Closeable { mBatteryTimeRemainingMs = source.readLong(); mChargeTimeRemainingMs = source.readLong(); mCustomPowerComponentNames = source.readStringArray(); - mIncludesPowerModels = source.readBoolean(); mIncludesProcessStateData = source.readBoolean(); mIncludesScreenStateData = source.readBoolean(); mIncludesPowerStateData = source.readBoolean(); mBatteryConsumersCursorWindow = CursorWindow.newFromParcel(source); mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout( - mCustomPowerComponentNames, mIncludesPowerModels, mIncludesProcessStateData, + mCustomPowerComponentNames, mIncludesProcessStateData, mIncludesScreenStateData, mIncludesPowerStateData); final int numRows = mBatteryConsumersCursorWindow.getNumRows(); @@ -424,7 +420,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { dest.writeLong(mBatteryTimeRemainingMs); dest.writeLong(mChargeTimeRemainingMs); dest.writeStringArray(mCustomPowerComponentNames); - dest.writeBoolean(mIncludesPowerModels); dest.writeBoolean(mIncludesProcessStateData); dest.writeBoolean(mIncludesScreenStateData); dest.writeBoolean(mIncludesPowerStateData); @@ -506,9 +501,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { getDischargeDurationMs()); deviceBatteryConsumer.writeStatsProto(proto, BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER); - if (mIncludesPowerModels) { - deviceBatteryConsumer.writePowerComponentModelProto(proto); - } writeUidBatteryConsumersProto(proto, maxRawSize); } @@ -629,7 +621,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { printPowerComponent(pw, prefix, mBatteryConsumerDataLayout.getPowerComponentName(powerComponent), - devicePowerMah, appsPowerMah, BatteryConsumer.POWER_MODEL_UNDEFINED, + devicePowerMah, appsPowerMah, deviceConsumer.getUsageDurationMillis(powerComponent)); } @@ -716,23 +708,15 @@ public final class BatteryUsageStats implements Parcelable, Closeable { printPowerComponent(pw, prefix, mBatteryConsumerDataLayout.getPowerComponentName(powerComponent), devicePowerMah, appsPowerMah, - mIncludesPowerModels ? deviceConsumer.getPowerModel(powerComponent) - : BatteryConsumer.POWER_MODEL_UNDEFINED, deviceConsumer.getUsageDurationMillis(dimensions)); } } private void printPowerComponent(PrintWriter pw, String prefix, String label, - double devicePowerMah, double appsPowerMah, int powerModel, long durationMs) { + double devicePowerMah, double appsPowerMah, long durationMs) { StringBuilder sb = new StringBuilder(); sb.append(prefix).append(" ").append(label).append(": ") .append(BatteryStats.formatCharge(devicePowerMah)); - if (powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED - && powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE) { - sb.append(" ["); - sb.append(BatteryConsumer.powerModelToString(powerModel)); - sb.append("]"); - } sb.append(" apps: ").append(BatteryStats.formatCharge(appsPowerMah)); if (durationMs != 0) { sb.append(" duration: "); @@ -828,7 +812,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { final boolean includesPowerStateData = parser.getAttributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA, false); - builder = new Builder(customComponentNames.toArray(new String[0]), true, + builder = new Builder(customComponentNames.toArray(new String[0]), includesProcStateData, includesScreenStateData, includesPowerStateData, 0); builder.setStatsStartTimestamp( @@ -913,7 +897,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private final CursorWindow mBatteryConsumersCursorWindow; @NonNull private final String[] mCustomPowerComponentNames; - private final boolean mIncludePowerModels; private final boolean mIncludesProcessStateData; private final boolean mIncludesScreenStateData; private final boolean mIncludesPowerStateData; @@ -938,22 +921,21 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private BatteryStatsHistory mBatteryStatsHistory; public Builder(@NonNull String[] customPowerComponentNames) { - this(customPowerComponentNames, false, false, false, false, 0); + this(customPowerComponentNames, false, false, false, 0); } - public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, + public Builder(@NonNull String[] customPowerComponentNames, boolean includeProcessStateData, boolean includeScreenStateData, boolean includesPowerStateData, double minConsumedPowerThreshold) { mBatteryConsumersCursorWindow = new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE); onCursorWindowAllocated(mBatteryConsumersCursorWindow); mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout( - customPowerComponentNames, includePowerModels, includeProcessStateData, + customPowerComponentNames, includeProcessStateData, includeScreenStateData, includesPowerStateData); mBatteryConsumersCursorWindow.setNumColumns(mBatteryConsumerDataLayout.columnCount); mCustomPowerComponentNames = customPowerComponentNames; - mIncludePowerModels = includePowerModels; mIncludesProcessStateData = includeProcessStateData; mIncludesScreenStateData = includeScreenStateData; mIncludesPowerStateData = includesPowerStateData; diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 6325b003a999..6e67578fadc8 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -24,6 +24,7 @@ import com.android.internal.os.MonotonicClock; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * Query parameters for the {@link BatteryStatsManager#getBatteryUsageStats()} call. @@ -65,12 +66,6 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 0x0002; - /** - * Indicates that identifiers of power models used for computations of power - * consumption should be included in the BatteryUsageStats. - */ - public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS = 0x0004; - public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA = 0x0008; public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS = 0x0010; @@ -202,7 +197,24 @@ public final class BatteryUsageStatsQuery implements Parcelable { return mAggregatedToTimestamp; } + @Override + public String toString() { + return "BatteryUsageStatsQuery{" + + "mFlags=" + Integer.toHexString(mFlags) + + ", mUserIds=" + Arrays.toString(mUserIds) + + ", mMaxStatsAgeMs=" + mMaxStatsAgeMs + + ", mAggregatedFromTimestamp=" + mAggregatedFromTimestamp + + ", mAggregatedToTimestamp=" + mAggregatedToTimestamp + + ", mMonotonicStartTime=" + mMonotonicStartTime + + ", mMonotonicEndTime=" + mMonotonicEndTime + + ", mMinConsumedPowerThreshold=" + mMinConsumedPowerThreshold + + ", mPowerComponents=" + Arrays.toString(mPowerComponents) + + '}'; + } + private BatteryUsageStatsQuery(Parcel in) { + mMonotonicStartTime = in.readLong(); + mMonotonicEndTime = in.readLong(); mFlags = in.readInt(); mUserIds = new int[in.readInt()]; in.readIntArray(mUserIds); @@ -215,6 +227,8 @@ public final class BatteryUsageStatsQuery implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mMonotonicStartTime); + dest.writeLong(mMonotonicEndTime); dest.writeInt(mFlags); dest.writeInt(mUserIds.length); dest.writeIntArray(mUserIds); @@ -311,7 +325,10 @@ public final class BatteryUsageStatsQuery implements Parcelable { * power monitoring data is available. * * Should only be used for testing and debugging. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public Builder powerProfileModeledOnly() { mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL; return this; @@ -322,9 +339,10 @@ public final class BatteryUsageStatsQuery implements Parcelable { * of power consumption. * * Should only be used for testing and debugging. + * @deprecated PowerModel is no longer supported */ + @Deprecated public Builder includePowerModels() { - mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS; return this; } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index c2e9260879a8..9b39c62ad03e 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1546,6 +1546,57 @@ public class Build { } /** + * Convert a major.minor version String like "36.1" to an int that + * represents both major and minor version. + * + * @param version the String to parse + * @return an int encoding the major and minor version + * @throws IllegalArgumentException if the string could not be converted into an int + * + * @hide + */ + @SuppressWarnings("FlaggedApi") // SDK_INT_MULTIPLIER is defined in this file + public static @SdkIntFull int parseFullVersion(@NonNull String version) { + int index = version.indexOf('.'); + int major; + int minor = 0; + try { + if (index == -1) { + major = Integer.parseInt(version); + } else { + major = Integer.parseInt(version.substring(0, index)); + minor = Integer.parseInt(version.substring(index + 1)); + } + if (major < 0 || minor < 0) { + throw new NumberFormatException(); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("failed to parse '" + version + + "' as a major.minor version code"); + } + return major * VERSION_CODES_FULL.SDK_INT_MULTIPLIER + minor; + } + + /** + * Convert an int representing a major.minor version like SDK_INT_FULL to a + * human readable string. The returned string is only intended for debug + * and error messages. + * + * @param version the int to convert to a string + * @return a String representing the same major.minor version as the int passed in + * @throws IllegalArgumentException if {@code version} is negative + * + * @hide + */ + public static String fullVersionToString(@SdkIntFull int version) { + if (version < 0) { + throw new IllegalArgumentException("failed to convert '" + version + + "' to string: not a valid major.minor version code"); + } + return String.format("%d.%d", getMajorSdkVersion(version), getMinorSdkVersion(version)); + } + + /** * The vendor API for 2024 Q2 * * <p>For Android 14-QPR3 and later, the vendor API level is completely decoupled from the SDK diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java index f0d4f7d8737f..8e78b7e355f9 100644 --- a/core/java/android/os/CpuHeadroomParams.java +++ b/core/java/android/os/CpuHeadroomParams.java @@ -18,10 +18,13 @@ package android.os; import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.health.SystemHealthManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}. @@ -54,6 +57,16 @@ public final class CpuHeadroomParams { public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; /** + * Minimum CPU headroom calculation window size. + */ + public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; + + /** + * Maximum CPU headroom calculation window size. + */ + public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; + + /** * Sets the headroom calculation type. * <p> * @@ -83,6 +96,63 @@ public final class CpuHeadroomParams { } /** + * Sets the headroom calculation window size in milliseconds. + * <p> + * + * @param windowMillis the window size in milliseconds, ranged from + * [{@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN}, + * {@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller + * the value, the larger fluctuation in value should be expected. The + * default value can be retrieved from the + * {@link #getCalculationWindowMillis}. The device will try to use the + * closest feasible window size to this param. + * @throws IllegalArgumentException if the window size is not in allowed range. + */ + public void setCalculationWindowMillis( + @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) { + if (windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN + || windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) { + throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); + } + mInternal.calculationWindowMillis = windowMillis; + } + + /** + * Gets the headroom calculation window size in milliseconds. + * <p> + * This will return the default value chosen by the device if not set. + */ + public @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) long getCalculationWindowMillis() { + return mInternal.calculationWindowMillis; + } + + /** + * Sets the thread TIDs to track. + * <p> + * The TIDs should belong to the same of the process that will the headroom call. And they + * should not have different core affinity. + * <p> + * If not set, the headroom will be based on the PID of the process making the call. + * + * @param tids non-empty list of TIDs, maximum 5. + * @throws IllegalArgumentException if the list size is not in allowed range or TID is not + * positive. + */ + public void setTids(@NonNull int... tids) { + if (tids.length == 0 || tids.length > 5) { + throw new IllegalArgumentException("Invalid number of TIDs: " + tids.length); + } + for (int tid : tids) { + if (tid <= 0) { + throw new IllegalArgumentException("Invalid TID: " + tid); + } + } + mInternal.tids = Arrays.copyOf(tids, tids.length); + } + + /** * @hide */ public CpuHeadroomParamsInternal getInternal() { diff --git a/core/java/android/os/CpuHeadroomParamsInternal.aidl b/core/java/android/os/CpuHeadroomParamsInternal.aidl index 6cc4699a809e..d572f965579b 100644 --- a/core/java/android/os/CpuHeadroomParamsInternal.aidl +++ b/core/java/android/os/CpuHeadroomParamsInternal.aidl @@ -25,6 +25,8 @@ import android.hardware.power.CpuHeadroomParams; @JavaDerive(equals = true, toString = true) parcelable CpuHeadroomParamsInternal { boolean usesDeviceHeadroom = false; + int[] tids; + int calculationWindowMillis = 1000; CpuHeadroomParams.CalculationType calculationType = CpuHeadroomParams.CalculationType.MIN; CpuHeadroomParams.SelectionType selectionType = CpuHeadroomParams.SelectionType.ALL; } diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java index efb2a28ad2b5..4dc98264e57b 100644 --- a/core/java/android/os/GpuHeadroomParams.java +++ b/core/java/android/os/GpuHeadroomParams.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; import android.os.health.SystemHealthManager; import java.lang.annotation.Retention; @@ -54,6 +55,16 @@ public final class GpuHeadroomParams { public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; /** + * Minimum GPU headroom calculation window size. + */ + public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; + + /** + * Maximum GPU headroom calculation window size. + */ + public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; + + /** * Sets the headroom calculation type. * <p> * @@ -83,6 +94,39 @@ public final class GpuHeadroomParams { } /** + * Sets the headroom calculation window size in milliseconds. + * <p> + * + * @param windowMillis the window size in milliseconds, ranged from + * [{@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN}, + * {@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller + * the value, the larger fluctuation in value should be expected. The + * default value can be retrieved from the + * {@link #getCalculationWindowMillis}. If the device will try to use the + * closest feasible window size to this param. + * @throws IllegalArgumentException if the window is invalid. + */ + public void setCalculationWindowMillis( + @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) { + if (windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN + || windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) { + throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); + } + mInternal.calculationWindowMillis = windowMillis; + } + + /** + * Gets the headroom calculation window size in milliseconds. + * <p> + * This will return the default value chosen by the device if not set. + */ + public @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int getCalculationWindowMillis() { + return mInternal.calculationWindowMillis; + } + + /** * @hide */ public GpuHeadroomParamsInternal getInternal() { diff --git a/core/java/android/os/GpuHeadroomParamsInternal.aidl b/core/java/android/os/GpuHeadroomParamsInternal.aidl index 20309e7673f2..40d5d8e409ef 100644 --- a/core/java/android/os/GpuHeadroomParamsInternal.aidl +++ b/core/java/android/os/GpuHeadroomParamsInternal.aidl @@ -24,5 +24,6 @@ import android.hardware.power.GpuHeadroomParams; */ @JavaDerive(equals = true, toString = true) parcelable GpuHeadroomParamsInternal { + int calculationWindowMillis = 1000; GpuHeadroomParams.CalculationType calculationType = GpuHeadroomParams.CalculationType.MIN; } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 94768d111ca4..8f6a50843ddb 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -98,6 +98,7 @@ public class GraphicsEnvironment { private static final int VULKAN_1_1 = 0x00401000; private static final int VULKAN_1_2 = 0x00402000; private static final int VULKAN_1_3 = 0x00403000; + private static final int VULKAN_1_4 = 0x00404000; // Values for UPDATABLE_DRIVER_ALL_APPS // 0: Default (Invalid values fallback to default as well) @@ -179,6 +180,10 @@ public class GraphicsEnvironment { private int getVulkanVersion(PackageManager pm) { // PackageManager doesn't have an API to retrieve the version of a specific feature, and we // need to avoid retrieving all system features here and looping through them. + if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_4)) { + return VULKAN_1_4; + } + if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_3)) { return VULKAN_1_3; } diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl index a043da9b7a36..f1936b5e0ff9 100644 --- a/core/java/android/os/IHintManager.aidl +++ b/core/java/android/os/IHintManager.aidl @@ -21,7 +21,9 @@ import android.os.CpuHeadroomParamsInternal; import android.os.GpuHeadroomParamsInternal; import android.os.IHintSession; import android.os.SessionCreationConfig; +import android.hardware.power.CpuHeadroomResult; import android.hardware.power.ChannelConfig; +import android.hardware.power.GpuHeadroomResult; import android.hardware.power.SessionConfig; import android.hardware.power.SessionTag; @@ -53,9 +55,9 @@ interface IHintManager { */ @nullable ChannelConfig getSessionChannel(in IBinder token); oneway void closeSessionChannel(); - float[] getCpuHeadroom(in CpuHeadroomParamsInternal params); + @nullable CpuHeadroomResult getCpuHeadroom(in CpuHeadroomParamsInternal params); long getCpuHeadroomMinIntervalMillis(); - float getGpuHeadroom(in GpuHeadroomParamsInternal params); + @nullable GpuHeadroomResult getGpuHeadroom(in GpuHeadroomParamsInternal params); long getGpuHeadroomMinIntervalMillis(); /** diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 94259d7cf819..f9789c19b0d5 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -91,6 +91,8 @@ per-file DdmSyncStageUpdater.java = sanglardf@google.com, rpaquay@google.com # PerformanceHintManager per-file CpuHeadroom*.aidl = file:/ADPF_OWNERS per-file GpuHeadroom*.aidl = file:/ADPF_OWNERS +per-file CpuHeadroom*.java = file:/ADPF_OWNERS +per-file GpuHeadroom*.java = file:/ADPF_OWNERS per-file PerformanceHintManager.java = file:/ADPF_OWNERS per-file WorkDuration.java = file:/ADPF_OWNERS per-file IHintManager.aidl = file:/ADPF_OWNERS diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index d116e0737c46..4db1f1b32407 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -15,7 +15,6 @@ */ package android.os; -import static android.os.BatteryConsumer.BatteryConsumerDataLayout.POWER_MODEL_NOT_INCLUDED; import static android.os.BatteryConsumer.POWER_COMPONENT_ANY; import static android.os.BatteryConsumer.POWER_COMPONENT_BASE; import static android.os.BatteryConsumer.POWER_STATE_ANY; @@ -156,15 +155,6 @@ class PowerComponents { return mData.layout.getPowerComponentName(componentId); } - @BatteryConsumer.PowerModel - int getPowerModel(BatteryConsumer.Key key) { - if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { - throw new IllegalStateException( - "Power model IDs were not requested in the BatteryUsageStatsQuery"); - } - return mData.getInt(key.mPowerModelColumnIndex); - } - /** * Returns the amount of time used by the specified component, e.g. CPU, WiFi etc. * @@ -378,10 +368,6 @@ class PowerComponents { if (durationMs != 0) { serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); } - if (mData.layout.powerModelsIncluded) { - serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL, - getPowerModel(key)); - } serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT); } serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); @@ -411,7 +397,6 @@ class PowerComponents { int powerState = POWER_STATE_UNSPECIFIED; double powerMah = 0; long durationMs = 0; - int model = BatteryConsumer.POWER_MODEL_UNDEFINED; for (int i = 0; i < parser.getAttributeCount(); i++) { switch (parser.getAttributeName(i)) { case BatteryUsageStats.XML_ATTR_ID: @@ -432,14 +417,11 @@ class PowerComponents { case BatteryUsageStats.XML_ATTR_DURATION: durationMs = parser.getAttributeLong(i); break; - case BatteryUsageStats.XML_ATTR_MODEL: - model = parser.getAttributeInt(i); - break; } } final BatteryConsumer.Key key = builder.mData.layout.getKey(componentId, processState, screenState, powerState); - builder.addConsumedPower(key, powerMah, model); + builder.addConsumedPower(key, powerMah); builder.addUsageDurationMillis(key, durationMs); break; } @@ -453,43 +435,28 @@ class PowerComponents { * Builder for PowerComponents. */ static final class Builder { - private static final byte POWER_MODEL_UNINITIALIZED = -1; - private final BatteryConsumer.BatteryConsumerData mData; private final double mMinConsumedPowerThreshold; Builder(BatteryConsumer.BatteryConsumerData data, double minConsumedPowerThreshold) { mData = data; mMinConsumedPowerThreshold = minConsumedPowerThreshold; - for (BatteryConsumer.Key key : mData.layout.keys) { - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - mData.putInt(key.mPowerModelColumnIndex, POWER_MODEL_UNINITIALIZED); - } - } } /** - * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double, int)} + * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double)} */ @Deprecated @NonNull - public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower, - int powerModel) { + public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower) { mData.putDouble(key.mPowerColumnIndex, componentPower); - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - mData.putInt(key.mPowerModelColumnIndex, powerModel); - } return this; } @NonNull - public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower, - int powerModel) { + public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower) { mData.putDouble(key.mPowerColumnIndex, mData.getDouble(key.mPowerColumnIndex) + componentPower); - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - mData.putInt(key.mPowerModelColumnIndex, powerModel); - } return this; } @@ -547,28 +514,6 @@ class PowerComponents { mData.getLong(key.mDurationColumnIndex) + otherData.getLong(otherKey.mDurationColumnIndex)); } - if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { - continue; - } - - boolean undefined = false; - if (otherKey.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { - undefined = true; - } else { - final int powerModel = mData.getInt(key.mPowerModelColumnIndex); - int otherPowerModel = otherData.getInt(otherKey.mPowerModelColumnIndex); - if (powerModel == POWER_MODEL_UNINITIALIZED) { - mData.putInt(key.mPowerModelColumnIndex, otherPowerModel); - } else if (powerModel != otherPowerModel - && otherPowerModel != POWER_MODEL_UNINITIALIZED) { - undefined = true; - } - } - - if (undefined) { - mData.putInt(key.mPowerModelColumnIndex, - BatteryConsumer.POWER_MODEL_UNDEFINED); - } } } @@ -594,13 +539,6 @@ class PowerComponents { @NonNull public PowerComponents build() { for (BatteryConsumer.Key key : mData.layout.keys) { - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) { - mData.putInt(key.mPowerModelColumnIndex, - BatteryConsumer.POWER_MODEL_UNDEFINED); - } - } - if (mMinConsumedPowerThreshold != 0) { if (mData.getDouble(key.mPowerColumnIndex) < mMinConsumedPowerThreshold) { mData.putDouble(key.mPowerColumnIndex, 0); diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index 4db9bc333e2b..cd79e416531a 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -23,6 +23,8 @@ import android.annotation.Nullable; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.hardware.power.CpuHeadroomResult; +import android.hardware.power.GpuHeadroomResult; import android.os.BatteryStats; import android.os.Build; import android.os.Bundle; @@ -110,15 +112,16 @@ public class SystemHealthManager { } /** - * Provides an estimate of global available CPU headroom of the calling thread. + * Provides an estimate of global available CPU headroom. * <p> * * @param params params to customize the CPU headroom calculation, null to use default params. - * @return a single value a {@code Float.NaN} if it's temporarily unavailable. + * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable. * A valid value is ranged from [0, 100], where 0 indicates no more CPU resources can be * granted. - * @throws UnsupportedOperationException if the API is unsupported or the request params can't - * be served. + * @throws UnsupportedOperationException if the API is unsupported. + * @throws SecurityException if the TIDs of the params don't belong to the same process. + * @throws IllegalStateException if the TIDs of the params don't have the same affinity setting. */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom( @@ -127,8 +130,12 @@ public class SystemHealthManager { throw new UnsupportedOperationException(); } try { - return mHintManager.getCpuHeadroom( - params != null ? params.getInternal() : new CpuHeadroomParamsInternal())[0]; + final CpuHeadroomResult ret = mHintManager.getCpuHeadroom( + params != null ? params.getInternal() : new CpuHeadroomParamsInternal()); + if (ret == null || ret.getTag() != CpuHeadroomResult.globalHeadroom) { + return Float.NaN; + } + return ret.getGlobalHeadroom(); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -144,8 +151,7 @@ public class SystemHealthManager { * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable. * A valid value is ranged from [0, 100], where 0 indicates no more GPU resources can be * granted. - * @throws UnsupportedOperationException if the API is unsupported or the request params can't - * be served. + * @throws UnsupportedOperationException if the API is unsupported. */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom( @@ -154,8 +160,12 @@ public class SystemHealthManager { throw new UnsupportedOperationException(); } try { - return mHintManager.getGpuHeadroom( + final GpuHeadroomResult ret = mHintManager.getGpuHeadroom( params != null ? params.getInternal() : new GpuHeadroomParamsInternal()); + if (ret == null || ret.getTag() != GpuHeadroomResult.globalHeadroom) { + return Float.NaN; + } + return ret.getGlobalHeadroom(); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index a653e0a493ee..a7195834e6bb 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -436,3 +436,12 @@ flag { description: "Use profile labels from UserManager for default app section titles to allow partner customization" bug: "358369931" } + +flag { + name: "wallet_role_cross_user_enabled" + is_exported: true + is_fixed_read_only: true + namespace: "wallet_integration" + description: "Enable the Wallet role within profiles" + bug: "356107987" +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d5b525884ac1..8d054f4b1750 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6374,6 +6374,14 @@ public final class Settings { public static final String LOCALE_PREFERENCES = "locale_preferences"; /** + * User can change the region from region settings. This records user's preferred region. + * + * E.g. : if user's locale is en-US, this will record US + * @hide + */ + public static final String PREFERRED_REGION = "preferred_region"; + + /** * Setting to enable camera flash notification feature. * <ul> * <li> 0 = Off @@ -6547,6 +6555,7 @@ public final class Settings { PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE); PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING); PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON); + PRIVATE_SETTINGS.add(PREFERRED_REGION); } /** diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig index 6c92991ceff6..2007a5f43f41 100644 --- a/core/java/android/security/responsible_apis_flags.aconfig +++ b/core/java/android/security/responsible_apis_flags.aconfig @@ -96,6 +96,29 @@ flag { } flag { + name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected" + namespace: "responsible_apis" + description: "Prevent intent redirect attacks by showing a toast if not yet collected" + bug: "361143368" + is_fixed_read_only: true +} + +flag { + name: "prevent_intent_redirect_throw_exception_if_nested_keys_not_collected" + namespace: "responsible_apis" + description: "Prevent intent redirect attacks by throwing exception if the intent does not collect nested keys" + bug: "361143368" +} + +flag { + name: "prevent_intent_redirect_collect_nested_keys_on_server_if_not_collected" + namespace: "responsible_apis" + description: "Prevent intent redirect attacks by collecting nested keys on server if not yet collected" + bug: "361143368" + is_fixed_read_only: true +} + +flag { name: "enable_intent_matching_flags" is_exported: true namespace: "permissions" @@ -110,3 +133,16 @@ flag { description: "Android Advanced Protection Mode Feature: Disable Install Unknown Sources" bug: "369361373" } + +flag { + name: "aapm_feature_memory_tagging_extension" + namespace: "responsible_apis" + description: "Android Advanced Protection Mode Feature: Memory Tagging Extension" + bug: "378931989" +} +flag { + name: "aapm_feature_disable_cellular_2g" + namespace: "responsible_apis" + description: "Android Advanced Protection Mode Feature: Disable Cellular 2G" + bug: "377748286" +} diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java index 14a14e69f208..fba8e42cc673 100644 --- a/core/java/android/service/autofill/FillEventHistory.java +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -170,6 +170,9 @@ public final class FillEventHistory implements Parcelable { } parcel.writeInt(event.mSaveDialogNotShowReason); parcel.writeInt(event.mUiType); + if (Flags.addLastFocusedIdToFillEventHistory()) { + parcel.writeParcelable(event.mFocusedId, 0); + } } } } @@ -375,6 +378,8 @@ public final class FillEventHistory implements Parcelable { @UiType private final int mUiType; + @Nullable private final AutofillId mFocusedId; + /** * Returns the type of the event. * @@ -388,7 +393,7 @@ public final class FillEventHistory implements Parcelable { @FlaggedApi(FLAG_AUTOFILL_W_METRICS) @Nullable public AutofillId getFocusedId() { - return null; + return mFocusedId; } /** @@ -624,6 +629,7 @@ public final class FillEventHistory implements Parcelable { * @param manuallyFilledDatasetIds The ids of datasets that had values matching the * respective entry on {@code manuallyFilledFieldIds}. * @param detectedFieldClassifications the field classification matches. + * @param focusedId the field which was focused at the time of event trigger * * @throws IllegalArgumentException If the length of {@code changedFieldIds} and * {@code changedDatasetIds} doesn't match. @@ -640,11 +646,12 @@ public final class FillEventHistory implements Parcelable { @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, - @Nullable FieldClassification[] detectedFieldClassifications) { + @Nullable FieldClassification[] detectedFieldClassifications, + @Nullable AutofillId focusedId) { this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - NO_SAVE_UI_REASON_NONE); + NO_SAVE_UI_REASON_NONE, focusedId); } /** @@ -665,6 +672,7 @@ public final class FillEventHistory implements Parcelable { * respective entry on {@code manuallyFilledFieldIds}. * @param detectedFieldClassifications the field classification matches. * @param saveDialogNotShowReason The reason why a save dialog was not shown. + * @param focusedId the field which was focused at the time of event trigger * * @throws IllegalArgumentException If the length of {@code changedFieldIds} and * {@code changedDatasetIds} doesn't match. @@ -682,11 +690,12 @@ public final class FillEventHistory implements Parcelable { @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications, - int saveDialogNotShowReason) { + int saveDialogNotShowReason, + @Nullable AutofillId focusedId) { this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, UI_TYPE_UNKNOWN); + saveDialogNotShowReason, UI_TYPE_UNKNOWN, focusedId); } /** @@ -708,6 +717,7 @@ public final class FillEventHistory implements Parcelable { * @param detectedFieldClassifications the field classification matches. * @param saveDialogNotShowReason The reason why a save dialog was not shown. * @param uiType The ui presentation type for fill suggestion. + * @param focusedId the field which was focused at the time of event trigger * * @throws IllegalArgumentException If the length of {@code changedFieldIds} and * {@code changedDatasetIds} doesn't match. @@ -725,7 +735,7 @@ public final class FillEventHistory implements Parcelable { @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications, - int saveDialogNotShowReason, int uiType) { + int saveDialogNotShowReason, int uiType, @Nullable AutofillId focusedId) { mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_VIEW_REQUESTED_AUTOFILL, "eventType"); mDatasetId = datasetId; @@ -756,6 +766,7 @@ public final class FillEventHistory implements Parcelable { NO_SAVE_UI_REASON_NONE, NO_SAVE_UI_REASON_DATASET_MATCH, "saveDialogNotShowReason"); mUiType = uiType; + mFocusedId = focusedId; } @Override @@ -852,13 +863,17 @@ public final class FillEventHistory implements Parcelable { : null; final int saveDialogNotShowReason = parcel.readInt(); final int uiType = parcel.readInt(); + AutofillId focusedId = null; + if (Flags.addLastFocusedIdToFillEventHistory()) { + focusedId = parcel.readParcelable(null, AutofillId.class); + } selection.addEvent(new Event(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, uiType)); + saveDialogNotShowReason, uiType, focusedId)); } return selection; } diff --git a/core/java/android/view/ScrollCaptureSearchResults.java b/core/java/android/view/ScrollCaptureSearchResults.java index 3469b9dc7103..b1ce949f9919 100644 --- a/core/java/android/view/ScrollCaptureSearchResults.java +++ b/core/java/android/view/ScrollCaptureSearchResults.java @@ -16,10 +16,16 @@ package android.view; +import static android.view.flags.Flags.scrollCaptureTargetZOrderFix; + +import static java.util.Comparator.comparing; import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElse; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UiThread; +import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; import android.util.IndentingPrintWriter; @@ -113,7 +119,9 @@ public final class ScrollCaptureSearchResults { private void signalComplete() { mComplete = true; - mTargets.sort(PRIORITY_ORDER); + if (!scrollCaptureTargetZOrderFix()) { + mTargets.sort(PRIORITY_ORDER); + } if (mOnCompleteListener != null) { mOnCompleteListener.run(); mOnCompleteListener = null; @@ -125,14 +133,73 @@ public final class ScrollCaptureSearchResults { return new ArrayList<>(mTargets); } + private Rect getScrollBoundsInWindow(@Nullable ScrollCaptureTarget target) { + if (target == null || target.getScrollBounds() == null) { + return new Rect(); + } + Rect windowRect = new Rect(target.getScrollBounds()); + Point windowPosition = target.getPositionInWindow(); + windowRect.offset(windowPosition.x, windowPosition.y); + return windowRect; + } + /** * Get the top ranked result out of all completed requests. * * @return the top ranked result */ + @Nullable public ScrollCaptureTarget getTopResult() { - ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0); - return target != null && target.getScrollBounds() != null ? target : null; + if (!scrollCaptureTargetZOrderFix()) { + ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0); + return target != null && target.getScrollBounds() != null ? target : null; + } + List<ScrollCaptureTarget> filtered = new ArrayList<>(); + + mTargets.removeIf(a -> nullOrEmpty(a.getScrollBounds())); + + // Remove scroll targets obscured or covered by other scrolling views. + nextTarget: + for (int i = 0; i < mTargets.size(); i++) { + ScrollCaptureTarget current = mTargets.get(i); + + View currentView = current.getContainingView(); + + // Nested scroll containers: + // Check if the next view is a child of the current. If so, skip the current. + if (i + 1 < mTargets.size()) { + ScrollCaptureTarget next = mTargets.get(i + 1); + View nextView = next.getContainingView(); + // Honor explicit include hint on parent as escape hatch (unless both have it) + if (isDescendant(currentView, nextView) + && (!hasIncludeHint(currentView) || hasIncludeHint(nextView))) { + continue; + } + } + + // Check if any views will be drawn partially or fully over this one. + for (int j = i + 1; j < mTargets.size(); j++) { + ScrollCaptureTarget above = mTargets.get(j); + if (Rect.intersects(getScrollBoundsInWindow(current), + getScrollBoundsInWindow(above))) { + continue nextTarget; + } + } + + filtered.add(current); + } + + // natural order, false->true + Comparator<ScrollCaptureTarget> byIncludeHintPresence = comparing( + ScrollCaptureSearchResults::hasIncludeHint); + + // natural order, smallest->largest area + Comparator<ScrollCaptureTarget> byArea = comparing( + target -> area(requireNonNullElse(target.getScrollBounds(), new Rect()))); + + // The top result is the last one (with include hint if present, then by largest area) + filtered.sort(byIncludeHintPresence.thenComparing(byArea)); + return filtered.isEmpty() ? null : filtered.getLast(); } private class SearchRequest implements Consumer<Rect> { @@ -226,6 +293,10 @@ public final class ScrollCaptureSearchResults { return r == null || r.isEmpty(); } + private static boolean hasIncludeHint(ScrollCaptureTarget target) { + return hasIncludeHint(target.getContainingView()); + } + private static boolean hasIncludeHint(View view) { return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0; } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 4a9916c007c4..949b667f0b7a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -20,6 +20,7 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static android.view.flags.Flags.FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API; import static android.view.flags.Flags.toolkitViewgroupSetRequestedFrameRateApi; +import static android.view.flags.Flags.scrollCaptureTargetZOrderFix; import android.animation.LayoutTransition; import android.annotation.CallSuper; @@ -7657,6 +7658,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @NonNull Rect localVisibleRect, @NonNull Point windowOffset, @NonNull Consumer<ScrollCaptureTarget> targets) { + // Only visible views can be captured. + if (getVisibility() != View.VISIBLE) { + return; + } + if (getClipToPadding() && !localVisibleRect.intersect(mPaddingLeft, mPaddingTop, (mRight - mLeft) - mPaddingRight, (mBottom - mTop) - mPaddingBottom)) { return; @@ -7665,19 +7671,39 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Dispatch to self first. super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); + final int childrenCount = mChildrenCount; + if (childrenCount == 0) { + return; + } + // Skip children if descendants excluded. if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) != 0) { return; } - final Rect tmpRect = getTempRect(); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); + + ArrayList<View> preorderedList = null; + boolean customOrder = false; + if (scrollCaptureTargetZOrderFix()) { + preorderedList = buildOrderedChildList(); + customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); + } + final View[] children = mChildren; + for (int i = 0; i < childrenCount; i++) { + View child; + if (scrollCaptureTargetZOrderFix()) { + // Traverse children in the same order they will be drawn (honors Z if set) + final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); + child = getAndVerifyPreorderedView(preorderedList, children, childIndex); + } else { + child = children[i]; + } + // Only visible views can be captured. if (child.getVisibility() != View.VISIBLE) { continue; } + // Offset the given rectangle (in parent's local coordinates) into child's coordinate // space and clip the result to the child View's bounds, padding and clipRect as needed. // If the resulting rectangle is not empty, the request is forwarded to the child. @@ -7706,6 +7732,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.dispatchScrollCaptureSearch(tmpRect, childWindowOffset, targets); } } + if (preorderedList != null) { + preorderedList.clear(); + } } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b6e114b3a3ca..a0feccd87a81 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1236,6 +1236,8 @@ public final class ViewRootImpl implements ViewParent, private @ActivityInfo.ColorMode int mCurrentColorMode = ActivityInfo.COLOR_MODE_DEFAULT; private long mColorModeLastSetMillis = -1; + private final boolean mIsSubscribeGranularDisplayEventsEnabled; + public ViewRootImpl(Context context, Display display) { this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout()); } @@ -1333,6 +1335,8 @@ public final class ViewRootImpl implements ViewParent, // Disable DRAW_WAKE_LOCK starting U. mDisableDrawWakeLock = CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK) && disableDrawWakeLock(); + mIsSubscribeGranularDisplayEventsEnabled = + com.android.server.display.feature.flags.Flags.subscribeGranularDisplayEvents(); } public static void addFirstDrawHandler(Runnable callback) { @@ -1810,14 +1814,22 @@ public final class ViewRootImpl implements ViewParent, mAccessibilityInteractionConnectionManager, mHandler); mAccessibilityManager.addHighContrastTextStateChangeListener( mExecutor, mHighContrastTextManager); + + + long eventsToBeRegistered = + (mIsSubscribeGranularDisplayEventsEnabled) + ? DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED + : DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED; DisplayManagerGlobal .getInstance() .registerDisplayListener( mDisplayListener, mHandler, - DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED - | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED - | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, + eventsToBeRegistered, mBasePackageName); if (forceInvertColor()) { diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 0dfaf4149ce5..88ccf88d40f6 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -959,6 +959,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par CONTENT_CHANGE_TYPE_CONTENT_INVALID, CONTENT_CHANGE_TYPE_ERROR, CONTENT_CHANGE_TYPE_ENABLED, + CONTENT_CHANGE_TYPE_CHECKED, + CONTENT_CHANGE_TYPE_EXPANDED, CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION, }) public @interface ContentChangeTypes {} @@ -1241,6 +1243,16 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR"; case CONTENT_CHANGE_TYPE_ENABLED: return "CONTENT_CHANGE_TYPE_ENABLED"; default: { + if (Flags.triStateChecked()) { + if (type == CONTENT_CHANGE_TYPE_CHECKED) { + return "CONTENT_CHANGE_TYPE_CHECKED"; + } + } + if (Flags.a11yExpansionStateApi()) { + if (type == CONTENT_CHANGE_TYPE_EXPANDED) { + return "CONTENT_CHANGE_TYPE_EXPANDED"; + } + } if (Flags.supplementalDescription()) { if (type == CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION) { return "CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION"; diff --git a/core/java/android/view/flags/scroll_capture.aconfig b/core/java/android/view/flags/scroll_capture.aconfig new file mode 100644 index 000000000000..fdf9c1ed8ad2 --- /dev/null +++ b/core/java/android/view/flags/scroll_capture.aconfig @@ -0,0 +1,13 @@ +package: "android.view.flags" +container: "system" + +flag { + name: "scroll_capture_target_z_order_fix" + namespace: "system_ui" + description: "Always prefer targets with higher z-order" + bug: "365969802" + metadata { + purpose: PURPOSE_BUGFIX + } +} + diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java index 0bf6380eb904..eb3b76873a8f 100644 --- a/core/java/android/widget/Button.java +++ b/core/java/android/widget/Button.java @@ -134,6 +134,7 @@ public class Button extends TextView { // 1. app target sdk is 36 or above. // 2. feature flag rolled-out. // 3. device is a watch. + // 4. button uses Theme.DeviceDefault. // getButtonDefaultStyleAttr and getButtonDefaultStyleRes works together to alter the UI // while considering the conditions above. // Their results are mutual exclusive. i.e. when conditions above are all true, @@ -229,6 +230,7 @@ public class Button extends TextView { private static boolean useWearMaterial3Style(Context context) { return Flags.useWearMaterial3Ui() && CompatChanges.isChangeEnabled(WEAR_MATERIAL3_BUTTON) - && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); + && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH) + && context.getThemeResId() == com.android.internal.R.style.Theme_DeviceDefault; } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 6e43d0f1c6cd..2a8a92882310 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -16,6 +16,8 @@ package android.widget; +import static android.view.accessibility.Flags.indeterminateRangeInfo; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -2364,15 +2366,22 @@ public class ProgressBar extends View { public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); - if (!isIndeterminate()) { - AccessibilityNodeInfo.RangeInfo rangeInfo = AccessibilityNodeInfo.RangeInfo.obtain( + AccessibilityNodeInfo.RangeInfo rangeInfo = null; + if (isIndeterminate()) { + if (indeterminateRangeInfo()) { + rangeInfo = AccessibilityNodeInfo.RangeInfo.INDETERMINATE; + } + } else { + rangeInfo = new AccessibilityNodeInfo.RangeInfo( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(), getProgress()); - info.setRangeInfo(rangeInfo); } - // Only set the default state description when custom state descripton is null. + info.setRangeInfo(rangeInfo); + + // Only set the default state description when custom state description is null. if (getStateDescription() == null) { + // TODO(b/380340432): Remove after accessibility services stop relying on this. if (isIndeterminate()) { info.setStateDescription(getResources().getString(R.string.in_progress)); } else { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 595eb260f998..7e3b90444429 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1298,7 +1298,13 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - if (mIntentId != -1 || mItems == null) { + if (mItems == null) { + // Null item indicates adapter conversion took place, so the URIs in cached items + // need to be validated. + RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId); + if (cachedItems != null) { + cachedItems.visitUris(visitor); + } return; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index cb70466fcd81..d7750bd412a3 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -106,7 +106,6 @@ import android.os.Parcelable; import android.os.ParcelableParcel; import android.os.Process; import android.os.SystemClock; -import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.text.BoringLayout; @@ -9230,179 +9229,174 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected void onDraw(Canvas canvas) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onDraw"); - try { - restartMarqueeIfNeeded(); - - // Draw the background for this view - super.onDraw(canvas); - - final int compoundPaddingLeft = getCompoundPaddingLeft(); - final int compoundPaddingTop = getCompoundPaddingTop(); - final int compoundPaddingRight = getCompoundPaddingRight(); - final int compoundPaddingBottom = getCompoundPaddingBottom(); - final int scrollX = mScrollX; - final int scrollY = mScrollY; - final int right = mRight; - final int left = mLeft; - final int bottom = mBottom; - final int top = mTop; - final boolean isLayoutRtl = isLayoutRtl(); - final int offset = getHorizontalOffsetForDrawables(); - final int leftOffset = isLayoutRtl ? 0 : offset; - final int rightOffset = isLayoutRtl ? offset : 0; - - final Drawables dr = mDrawables; - if (dr != null) { - /* - * Compound, not extended, because the icon is not clipped - * if the text height is smaller. - */ + restartMarqueeIfNeeded(); - int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop; - int hspace = right - left - compoundPaddingRight - compoundPaddingLeft; - - // IMPORTANT: The coordinates computed are also used in invalidateDrawable() - // Make sure to update invalidateDrawable() when changing this code. - if (dr.mShowing[Drawables.LEFT] != null) { - canvas.save(); - canvas.translate(scrollX + mPaddingLeft + leftOffset, - scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2); - dr.mShowing[Drawables.LEFT].draw(canvas); - canvas.restore(); - } + // Draw the background for this view + super.onDraw(canvas); + + final int compoundPaddingLeft = getCompoundPaddingLeft(); + final int compoundPaddingTop = getCompoundPaddingTop(); + final int compoundPaddingRight = getCompoundPaddingRight(); + final int compoundPaddingBottom = getCompoundPaddingBottom(); + final int scrollX = mScrollX; + final int scrollY = mScrollY; + final int right = mRight; + final int left = mLeft; + final int bottom = mBottom; + final int top = mTop; + final boolean isLayoutRtl = isLayoutRtl(); + final int offset = getHorizontalOffsetForDrawables(); + final int leftOffset = isLayoutRtl ? 0 : offset; + final int rightOffset = isLayoutRtl ? offset : 0; - // IMPORTANT: The coordinates computed are also used in invalidateDrawable() - // Make sure to update invalidateDrawable() when changing this code. - if (dr.mShowing[Drawables.RIGHT] != null) { - canvas.save(); - canvas.translate(scrollX + right - left - mPaddingRight - - dr.mDrawableSizeRight - rightOffset, - scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2); - dr.mShowing[Drawables.RIGHT].draw(canvas); - canvas.restore(); - } + final Drawables dr = mDrawables; + if (dr != null) { + /* + * Compound, not extended, because the icon is not clipped + * if the text height is smaller. + */ - // IMPORTANT: The coordinates computed are also used in invalidateDrawable() - // Make sure to update invalidateDrawable() when changing this code. - if (dr.mShowing[Drawables.TOP] != null) { - canvas.save(); - canvas.translate(scrollX + compoundPaddingLeft - + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop); - dr.mShowing[Drawables.TOP].draw(canvas); - canvas.restore(); - } + int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop; + int hspace = right - left - compoundPaddingRight - compoundPaddingLeft; - // IMPORTANT: The coordinates computed are also used in invalidateDrawable() - // Make sure to update invalidateDrawable() when changing this code. - if (dr.mShowing[Drawables.BOTTOM] != null) { - canvas.save(); - canvas.translate(scrollX + compoundPaddingLeft - + (hspace - dr.mDrawableWidthBottom) / 2, - scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom); - dr.mShowing[Drawables.BOTTOM].draw(canvas); - canvas.restore(); - } + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. + if (dr.mShowing[Drawables.LEFT] != null) { + canvas.save(); + canvas.translate(scrollX + mPaddingLeft + leftOffset, + scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2); + dr.mShowing[Drawables.LEFT].draw(canvas); + canvas.restore(); } - int color = mCurTextColor; + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. + if (dr.mShowing[Drawables.RIGHT] != null) { + canvas.save(); + canvas.translate(scrollX + right - left - mPaddingRight + - dr.mDrawableSizeRight - rightOffset, + scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2); + dr.mShowing[Drawables.RIGHT].draw(canvas); + canvas.restore(); + } - if (mLayout == null) { - assumeLayout(); + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. + if (dr.mShowing[Drawables.TOP] != null) { + canvas.save(); + canvas.translate(scrollX + compoundPaddingLeft + + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop); + dr.mShowing[Drawables.TOP].draw(canvas); + canvas.restore(); } - Layout layout = mLayout; + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. + if (dr.mShowing[Drawables.BOTTOM] != null) { + canvas.save(); + canvas.translate(scrollX + compoundPaddingLeft + + (hspace - dr.mDrawableWidthBottom) / 2, + scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom); + dr.mShowing[Drawables.BOTTOM].draw(canvas); + canvas.restore(); + } + } - if (mHint != null && !mHideHint && mText.length() == 0) { - if (mHintTextColor != null) { - color = mCurHintTextColor; - } + int color = mCurTextColor; - layout = mHintLayout; - } + if (mLayout == null) { + assumeLayout(); + } - mTextPaint.setColor(color); - mTextPaint.drawableState = getDrawableState(); + Layout layout = mLayout; - canvas.save(); - /* Would be faster if we didn't have to do this. Can we chop the - (displayable) text so that we don't need to do this ever? - */ + if (mHint != null && !mHideHint && mText.length() == 0) { + if (mHintTextColor != null) { + color = mCurHintTextColor; + } - int extendedPaddingTop = getExtendedPaddingTop(); - int extendedPaddingBottom = getExtendedPaddingBottom(); + layout = mHintLayout; + } - final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; - final int maxScrollY = mLayout.getHeight() - vspace; + mTextPaint.setColor(color); + mTextPaint.drawableState = getDrawableState(); - float clipLeft = compoundPaddingLeft + scrollX; - float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY; - float clipRight = right - left - getCompoundPaddingRight() + scrollX; - float clipBottom = bottom - top + scrollY - - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom); + canvas.save(); + /* Would be faster if we didn't have to do this. Can we chop the + (displayable) text so that we don't need to do this ever? + */ - if (mShadowRadius != 0) { - clipLeft += Math.min(0, mShadowDx - mShadowRadius); - clipRight += Math.max(0, mShadowDx + mShadowRadius); + int extendedPaddingTop = getExtendedPaddingTop(); + int extendedPaddingBottom = getExtendedPaddingBottom(); - clipTop += Math.min(0, mShadowDy - mShadowRadius); - clipBottom += Math.max(0, mShadowDy + mShadowRadius); - } + final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; + final int maxScrollY = mLayout.getHeight() - vspace; - canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom); + float clipLeft = compoundPaddingLeft + scrollX; + float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY; + float clipRight = right - left - getCompoundPaddingRight() + scrollX; + float clipBottom = bottom - top + scrollY + - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom); - int voffsetText = 0; - int voffsetCursor = 0; + if (mShadowRadius != 0) { + clipLeft += Math.min(0, mShadowDx - mShadowRadius); + clipRight += Math.max(0, mShadowDx + mShadowRadius); - // translate in by our padding - /* shortcircuit calling getVerticaOffset() */ - if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { - voffsetText = getVerticalOffset(false); - voffsetCursor = getVerticalOffset(true); - } - canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText); - - final int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - if (isMarqueeFadeEnabled()) { - if (!mSingleLine && getLineCount() == 1 && canMarquee() - && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) { - final int width = mRight - mLeft; - final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight(); - final float dx = mLayout.getLineRight(0) - (width - padding); - canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); - } + clipTop += Math.min(0, mShadowDy - mShadowRadius); + clipBottom += Math.max(0, mShadowDy + mShadowRadius); + } - if (mMarquee != null && mMarquee.isRunning()) { - final float dx = -mMarquee.getScroll(); - canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); - } - } + canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom); - final int cursorOffsetVertical = voffsetCursor - voffsetText; + int voffsetText = 0; + int voffsetCursor = 0; - maybeUpdateHighlightPaths(); - // If there is a gesture preview highlight, then the selection or cursor is not drawn. - Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath(); - if (mEditor != null) { - mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight, - mHighlightPaint, cursorOffsetVertical); - } else { - layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint, - cursorOffsetVertical); + // translate in by our padding + /* shortcircuit calling getVerticaOffset() */ + if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { + voffsetText = getVerticalOffset(false); + voffsetCursor = getVerticalOffset(true); + } + canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText); + + final int layoutDirection = getLayoutDirection(); + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); + if (isMarqueeFadeEnabled()) { + if (!mSingleLine && getLineCount() == 1 && canMarquee() + && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) { + final int width = mRight - mLeft; + final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight(); + final float dx = mLayout.getLineRight(0) - (width - padding); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); } - if (mMarquee != null && mMarquee.shouldDrawGhost()) { - final float dx = mMarquee.getGhostOffset(); + if (mMarquee != null && mMarquee.isRunning()) { + final float dx = -mMarquee.getScroll(); canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); - layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint, - cursorOffsetVertical); } + } + + final int cursorOffsetVertical = voffsetCursor - voffsetText; + + maybeUpdateHighlightPaths(); + // If there is a gesture preview highlight, then the selection or cursor is not drawn. + Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath(); + if (mEditor != null) { + mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight, + mHighlightPaint, cursorOffsetVertical); + } else { + layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint, + cursorOffsetVertical); + } - canvas.restore(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); + if (mMarquee != null && mMarquee.shouldDrawGhost()) { + final float dx = mMarquee.getGhostOffset(); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); + layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint, + cursorOffsetVertical); } + + canvas.restore(); } @Override @@ -11260,201 +11254,192 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onMeasure"); - try { - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); - int width; - int height; + int width; + int height; - BoringLayout.Metrics boring = UNKNOWN_BORING; - BoringLayout.Metrics hintBoring = UNKNOWN_BORING; + BoringLayout.Metrics boring = UNKNOWN_BORING; + BoringLayout.Metrics hintBoring = UNKNOWN_BORING; - if (mTextDir == null) { - mTextDir = getTextDirectionHeuristic(); + if (mTextDir == null) { + mTextDir = getTextDirectionHeuristic(); + } + + int des = -1; + boolean fromexisting = false; + final float widthLimit = (widthMode == MeasureSpec.AT_MOST) + ? (float) widthSize : Float.MAX_VALUE; + + if (widthMode == MeasureSpec.EXACTLY) { + // Parent has told us how big to be. So be it. + width = widthSize; + } else { + if (mLayout != null && mEllipsize == null) { + des = desired(mLayout, mUseBoundsForWidth); } - int des = -1; - boolean fromexisting = false; - final float widthLimit = (widthMode == MeasureSpec.AT_MOST) - ? (float) widthSize : Float.MAX_VALUE; + if (des < 0) { + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, + isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(), + mBoring); + if (boring != null) { + mBoring = boring; + } + } else { + fromexisting = true; + } - if (widthMode == MeasureSpec.EXACTLY) { - // Parent has told us how big to be. So be it. - width = widthSize; + if (boring == null || boring == UNKNOWN_BORING) { + if (des < 0) { + des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0, + mTransformed.length(), mTextPaint, mTextDir, widthLimit, + mUseBoundsForWidth)); + } + width = des; } else { - if (mLayout != null && mEllipsize == null) { - des = desired(mLayout, mUseBoundsForWidth); + if (mUseBoundsForWidth) { + RectF bbox = boring.getDrawingBoundingBox(); + float rightMax = Math.max(bbox.right, boring.width); + float leftMin = Math.min(bbox.left, 0); + width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin)); + } else { + width = boring.width; } + } - if (des < 0) { - boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, + final Drawables dr = mDrawables; + if (dr != null) { + width = Math.max(width, dr.mDrawableWidthTop); + width = Math.max(width, dr.mDrawableWidthBottom); + } + + if (mHint != null) { + int hintDes = -1; + int hintWidth; + + if (mHintLayout != null && mEllipsize == null) { + hintDes = desired(mHintLayout, mUseBoundsForWidth); + } + + if (hintDes < 0) { + hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(), - mBoring); - if (boring != null) { - mBoring = boring; + mHintBoring); + if (hintBoring != null) { + mHintBoring = hintBoring; } - } else { - fromexisting = true; } - if (boring == null || boring == UNKNOWN_BORING) { - if (des < 0) { - des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0, - mTransformed.length(), mTextPaint, mTextDir, widthLimit, + if (hintBoring == null || hintBoring == UNKNOWN_BORING) { + if (hintDes < 0) { + hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0, + mHint.length(), mTextPaint, mTextDir, widthLimit, mUseBoundsForWidth)); } - width = des; + hintWidth = hintDes; } else { - if (mUseBoundsForWidth) { - RectF bbox = boring.getDrawingBoundingBox(); - float rightMax = Math.max(bbox.right, boring.width); - float leftMin = Math.min(bbox.left, 0); - width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin)); - } else { - width = boring.width; - } + hintWidth = hintBoring.width; } - final Drawables dr = mDrawables; - if (dr != null) { - width = Math.max(width, dr.mDrawableWidthTop); - width = Math.max(width, dr.mDrawableWidthBottom); + if (hintWidth > width) { + width = hintWidth; } + } - if (mHint != null) { - int hintDes = -1; - int hintWidth; - - if (mHintLayout != null && mEllipsize == null) { - hintDes = desired(mHintLayout, mUseBoundsForWidth); - } + width += getCompoundPaddingLeft() + getCompoundPaddingRight(); - if (hintDes < 0) { - hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, - isFallbackLineSpacingForBoringLayout(), - getResolvedMinimumFontMetrics(), - mHintBoring); - if (hintBoring != null) { - mHintBoring = hintBoring; - } - } - - if (hintBoring == null || hintBoring == UNKNOWN_BORING) { - if (hintDes < 0) { - hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0, - mHint.length(), mTextPaint, mTextDir, widthLimit, - mUseBoundsForWidth)); - } - hintWidth = hintDes; - } else { - hintWidth = hintBoring.width; - } + if (mMaxWidthMode == EMS) { + width = Math.min(width, mMaxWidth * getLineHeight()); + } else { + width = Math.min(width, mMaxWidth); + } - if (hintWidth > width) { - width = hintWidth; - } - } + if (mMinWidthMode == EMS) { + width = Math.max(width, mMinWidth * getLineHeight()); + } else { + width = Math.max(width, mMinWidth); + } - width += getCompoundPaddingLeft() + getCompoundPaddingRight(); + // Check against our minimum width + width = Math.max(width, getSuggestedMinimumWidth()); - if (mMaxWidthMode == EMS) { - width = Math.min(width, mMaxWidth * getLineHeight()); - } else { - width = Math.min(width, mMaxWidth); - } + if (widthMode == MeasureSpec.AT_MOST) { + width = Math.min(widthSize, width); + } + } - if (mMinWidthMode == EMS) { - width = Math.max(width, mMinWidth * getLineHeight()); - } else { - width = Math.max(width, mMinWidth); - } + int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight(); + int unpaddedWidth = want; - // Check against our minimum width - width = Math.max(width, getSuggestedMinimumWidth()); + if (mHorizontallyScrolling) want = VERY_WIDE; - if (widthMode == MeasureSpec.AT_MOST) { - width = Math.min(widthSize, width); - } - } + int hintWant = want; + int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth(); - int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight(); - int unpaddedWidth = want; + if (mLayout == null) { + makeNewLayout(want, hintWant, boring, hintBoring, + width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false); + } else { + final boolean layoutChanged = (mLayout.getWidth() != want) || (hintWidth != hintWant) + || (mLayout.getEllipsizedWidth() + != width - getCompoundPaddingLeft() - getCompoundPaddingRight()); - if (mHorizontallyScrolling) want = VERY_WIDE; + final boolean widthChanged = (mHint == null) && (mEllipsize == null) + && (want > mLayout.getWidth()) + && (mLayout instanceof BoringLayout + || (fromexisting && des >= 0 && des <= want)); - int hintWant = want; - int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth(); + final boolean maximumChanged = (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum); - if (mLayout == null) { - makeNewLayout(want, hintWant, boring, hintBoring, - width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false); - } else { - final boolean layoutChanged = - (mLayout.getWidth() != want) || (hintWidth != hintWant) - || (mLayout.getEllipsizedWidth() - != width - getCompoundPaddingLeft() - getCompoundPaddingRight()); - - final boolean widthChanged = (mHint == null) && (mEllipsize == null) - && (want > mLayout.getWidth()) - && (mLayout instanceof BoringLayout - || (fromexisting && des >= 0 && des <= want)); - - final boolean maximumChanged = - (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum); - - if (layoutChanged || maximumChanged) { - if (!maximumChanged && widthChanged) { - mLayout.increaseWidthTo(want); - } else { - makeNewLayout(want, hintWant, boring, hintBoring, - width - getCompoundPaddingLeft() - getCompoundPaddingRight(), - false); - } + if (layoutChanged || maximumChanged) { + if (!maximumChanged && widthChanged) { + mLayout.increaseWidthTo(want); } else { - // Nothing has changed + makeNewLayout(want, hintWant, boring, hintBoring, + width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false); } - } - - if (heightMode == MeasureSpec.EXACTLY) { - // Parent has told us how big to be. So be it. - height = heightSize; - mDesiredHeightAtMeasure = -1; } else { - int desired = getDesiredHeight(); + // Nothing has changed + } + } - height = desired; - mDesiredHeightAtMeasure = desired; + if (heightMode == MeasureSpec.EXACTLY) { + // Parent has told us how big to be. So be it. + height = heightSize; + mDesiredHeightAtMeasure = -1; + } else { + int desired = getDesiredHeight(); - if (heightMode == MeasureSpec.AT_MOST) { - height = Math.min(desired, heightSize); - } - } + height = desired; + mDesiredHeightAtMeasure = desired; - int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom(); - if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) { - unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum)); + if (heightMode == MeasureSpec.AT_MOST) { + height = Math.min(desired, heightSize); } + } - /* - * We didn't let makeNewLayout() register to bring the cursor into view, - * so do it here if there is any possibility that it is needed. - */ - if (mMovement != null - || mLayout.getWidth() > unpaddedWidth - || mLayout.getHeight() > unpaddedHeight) { - registerForPreDraw(); - } else { - scrollTo(0, 0); - } + int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom(); + if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) { + unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum)); + } - setMeasuredDimension(width, height); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); + /* + * We didn't let makeNewLayout() register to bring the cursor into view, + * so do it here if there is any possibility that it is needed. + */ + if (mMovement != null + || mLayout.getWidth() > unpaddedWidth + || mLayout.getHeight() > unpaddedHeight) { + registerForPreDraw(); + } else { + scrollTo(0, 0); } + + setMeasuredDimension(width, height); } /** diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java index fdaab66f2de3..2b370b9797e5 100644 --- a/core/java/android/window/WindowContext.java +++ b/core/java/android/window/WindowContext.java @@ -94,6 +94,23 @@ public class WindowContext extends ContextWrapper implements WindowProvider { mController.attachToDisplayArea(mType, getDisplayId(), mOptions); } + /** + * Updates this context to a new displayId. + * <p> + * Note that this doesn't re-parent previously attached windows (they should be removed and + * re-added manually after this is called). Resources associated with this context will have + * the correct value and configuration for the new display after this is called. + */ + @Override + public void updateDisplay(int displayId) { + if (displayId == getDisplayId()) { + return; + } + super.updateDisplay(displayId); + mController.detachIfNeeded(); + mController.attachToDisplayArea(mType, displayId, mOptions); + } + @Override public Object getSystemService(String name) { if (WINDOW_SERVICE.equals(name)) { diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 8019e6791cf1..7faa5d702d6b 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -242,13 +242,6 @@ flag { } flag { - name: "enable_desktop_windowing_app_handle_education_integration" - namespace: "lse_desktop_experience" - description: "Enables desktop windowing app handle education and integrates new APIs" - bug: "380272815" -} - -flag { name: "enable_desktop_windowing_transitions" namespace: "lse_desktop_experience" description: "Enables desktop windowing transition & motion polish changes" @@ -304,6 +297,13 @@ flag { } flag { + name: "enable_desktop_windowing_app_to_web_education_integration" + namespace: "lse_desktop_experience" + description: "Enables desktop windowing App-to-Web education and integrates new APIs" + bug: "380272815" +} + +flag { name: "enable_minimize_button" namespace: "lse_desktop_experience" description: "Adds a minimize button the the caption bar" diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index e8831ec2743e..e2be1f57b1fb 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -360,43 +360,44 @@ public class AccessibilityShortcutController { // Avoid non-a11y users accidentally turning shortcut on without reading this carefully. // Put "don't turn on" as the primary action. - final AlertDialog alertDialog = mFrameworkObjectProvider.getAlertDialogBuilder( - // Use SystemUI context so we pick up any theme set in a vendor overlay - mFrameworkObjectProvider.getSystemUiContext()) - .setTitle(getShortcutWarningTitle(targets)) - .setMessage(getShortcutWarningMessage(targets)) - .setCancelable(false) - .setNegativeButton(R.string.accessibility_shortcut_on, - (DialogInterface d, int which) -> enableDefaultHardwareShortcut(userId)) - .setPositiveButton(R.string.accessibility_shortcut_off, - (DialogInterface d, int which) -> { - Set<String> targetServices = - ShortcutUtils.getShortcutTargetsFromSettings( - mContext, - HARDWARE, + final AlertDialog alertDialog = + mFrameworkObjectProvider + .getAlertDialogBuilder( + // Use SystemUI context so we pick up any theme set in a vendor + // overlay + mFrameworkObjectProvider.getSystemUiContext()) + .setTitle(getShortcutWarningTitle(targets)) + .setMessage(getShortcutWarningMessage(targets)) + .setCancelable(false) + .setNegativeButton( + R.string.accessibility_shortcut_on, + (DialogInterface d, int which) -> + enableDefaultHardwareShortcut(userId)) + .setPositiveButton( + R.string.accessibility_shortcut_off, + (DialogInterface d, int which) -> { + Set<String> targetServices = + ShortcutUtils.getShortcutTargetsFromSettings( + mContext, HARDWARE, userId); + am.enableShortcutsForTargets( + false, HARDWARE, targetServices, userId); + // If canceled, treat as if the dialog has never been shown + Settings.Secure.putIntForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + DialogStatus.NOT_SHOWN, userId); - if (Flags.migrateEnableShortcuts()) { - am.enableShortcutsForTargets( - false, HARDWARE, targetServices, userId); - } else { - Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "", - userId); - ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState( - mContext, targetServices, userId); - } - // If canceled, treat as if the dialog has never been shown - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - DialogStatus.NOT_SHOWN, userId); - }) - .setOnCancelListener((DialogInterface d) -> { - // If canceled, treat as if the dialog has never been shown - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - DialogStatus.NOT_SHOWN, userId); - }) - .create(); + }) + .setOnCancelListener( + (DialogInterface d) -> { + // If canceled, treat as if the dialog has never been shown + Settings.Secure.putIntForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + DialogStatus.NOT_SHOWN, + userId); + }) + .create(); return alertDialog; } @@ -531,14 +532,8 @@ public class AccessibilityShortcutController { // Default service is invalid, so nothing we can do here. return; } - if (Flags.migrateEnableShortcuts()) { - accessibilityManager.enableShortcutsForTargets(true, HARDWARE, - Set.of(defaultServiceComponent.flattenToString()), userId); - } else { - Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, - defaultServiceComponent.flattenToString(), userId); - } + accessibilityManager.enableShortcutsForTargets(true, HARDWARE, + Set.of(defaultServiceComponent.flattenToString()), userId); } private boolean performTtsPrompt(AlertDialog alertDialog) { diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java index a753110edd51..f0582d063c71 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java @@ -19,8 +19,6 @@ package com.android.internal.accessibility.dialog; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; -import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings; -import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,7 +29,6 @@ import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.view.View; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.Flags; import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; @@ -118,18 +115,9 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS @Override public void onCheckedChanged(boolean isChecked) { setShortcutEnabled(isChecked); - if (Flags.migrateEnableShortcuts()) { - final AccessibilityManager am = - getContext().getSystemService(AccessibilityManager.class); - am.enableShortcutsForTargets( - isChecked, getShortcutType(), Set.of(mId), UserHandle.myUserId()); - } else { - if (isChecked) { - optInValueToSettings(getContext(), getShortcutType(), getId()); - } else { - optOutValueFromSettings(getContext(), getShortcutType(), getId()); - } - } + final AccessibilityManager am = getContext().getSystemService(AccessibilityManager.class); + am.enableShortcutsForTargets( + isChecked, getShortcutType(), Set.of(mId), UserHandle.myUserId()); } public void setStateDescription(CharSequence stateDescription) { diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index ac4c066a992f..5fbb92de46a0 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -16,6 +16,7 @@ package com.android.internal.inputmethod; +import android.graphics.Region; import android.net.Uri; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethodSubtype; @@ -51,4 +52,5 @@ oneway interface IInputMethodPrivilegedOperations { void resetStylusHandwriting(int requestId); void switchKeyboardLayoutAsync(int direction); void setHandwritingSurfaceNotTouchable(boolean notTouchable); + void setHandwritingTouchableRegion(in Region region); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index b009c99dc9e3..36333a993c44 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -20,6 +20,7 @@ import android.annotation.AnyThread; import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Region; import android.inputmethodservice.InputMethodService.BackDispositionMode; import android.inputmethodservice.InputMethodService.ImeWindowVisibility; import android.net.Uri; @@ -159,6 +160,25 @@ public final class InputMethodPrivilegedOperations { } } + + /** + * Calls {@link IInputMethodPrivilegedOperations#setHandwritingTouchableRegion(Region)}. + * + * @param region {@link Region} to set handwritable. + */ + @AnyThread + public void setHandwritingTouchableRegion(Region region) { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.setHandwritingTouchableRegion(region); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String, * AndroidFuture)}. diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java index 1204ef3f48fc..fc415377f1ee 100644 --- a/core/java/com/android/internal/jank/Cuj.java +++ b/core/java/com/android/internal/jank/Cuj.java @@ -235,8 +235,19 @@ public class Cuj { */ public static final int CUJ_DESKTOP_MODE_SNAP_RESIZE = 118; + /** + * Track unmaximize window interaction in desktop mode. + * + * <p> Tracking starts when the maximize button or option is clicked {@link + * com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel#onClick} + * and finishes when the animation finishes {@link + * com.android.wm.shell.windowdecor.ToggleResizeDesktopTaskTransitionHandler#startAnimation} + * </p> + */ + public static final int CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW = 119; + // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE. - @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_SNAP_RESIZE; + @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW; /** @hide */ @IntDef({ @@ -346,7 +357,8 @@ public class Cuj { CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH, CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE, - CUJ_DESKTOP_MODE_SNAP_RESIZE + CUJ_DESKTOP_MODE_SNAP_RESIZE, + CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW }) @Retention(RetentionPolicy.SOURCE) public @interface CujType {} @@ -467,6 +479,7 @@ public class Cuj { CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_SNAP_RESIZE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_SNAP_RESIZE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_UNMAXIMIZE_WINDOW; } private Cuj() { @@ -699,6 +712,8 @@ public class Cuj { return "DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE"; case CUJ_DESKTOP_MODE_SNAP_RESIZE: return "DESKTOP_MODE_SNAP_RESIZE"; + case CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW: + return "DESKTOP_MODE_UNMAXIMIZE_WINDOW"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java index 48d0d6c777de..5ec5762c0533 100644 --- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java +++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java @@ -392,6 +392,10 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, private int memtagMode; @ApplicationInfo.NativeHeapZeroInitialized private int nativeHeapZeroInitialized; + + @ApplicationInfo.PageSizeAppCompatFlags private int mPageSizeAppCompatFlags = + ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED; + @Nullable @DataClass.ParcelWith(Parcelling.BuiltIn.ForBoolean.class) private Boolean requestRawExternalStorageAccess; @@ -1118,6 +1122,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, return nativeHeapZeroInitialized; } + @ApplicationInfo.PageSizeAppCompatFlags + @Override + public int getPageSizeAppCompatFlags() { + return mPageSizeAppCompatFlags; + } + @Override public int getNetworkSecurityConfigResourceId() { return networkSecurityConfigRes; @@ -2221,6 +2231,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, } @Override + public PackageImpl setPageSizeAppCompatFlags(@ApplicationInfo.PageSizeAppCompatFlags int flag) { + mPageSizeAppCompatFlags = flag; + return this; + } + + @Override public PackageImpl setNetworkSecurityConfigResourceId(int value) { networkSecurityConfigRes = value; return this; @@ -2703,6 +2719,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts); } appInfo.allowCrossUidActivitySwitchFromBelow = mAllowCrossUidActivitySwitchFromBelow; + appInfo.setPageSizeAppCompatFlags(mPageSizeAppCompatFlags); return appInfo; } @@ -3305,6 +3322,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, dest.writeInt(this.mIntentMatchingFlags); dest.writeIntArray(this.mAlternateLauncherIconResIds); dest.writeIntArray(this.mAlternateLauncherLabelResIds); + dest.writeInt(this.mPageSizeAppCompatFlags); } private void writeFeatureFlagState(@NonNull Parcel dest) { @@ -3499,6 +3517,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, this.mIntentMatchingFlags = in.readInt(); this.mAlternateLauncherIconResIds = in.createIntArray(); this.mAlternateLauncherLabelResIds = in.createIntArray(); + this.mPageSizeAppCompatFlags = in.readInt(); assignDerivedFields(); assignDerivedFields2(); diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java index 67b985a61455..5062d58d4dca 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java @@ -31,7 +31,6 @@ import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseIntArray; -import com.android.internal.R; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedApexSystemService; @@ -280,6 +279,9 @@ public interface ParsingPackage { ParsingPackage setNativeHeapZeroInitialized( @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized); + /** Manifest option pageSizeCompat will populate this field */ + ParsingPackage setPageSizeAppCompatFlags(@ApplicationInfo.PageSizeAppCompatFlags int value); + ParsingPackage setRequestRawExternalStorageAccess( @Nullable Boolean requestRawExternalStorageAccess); diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java index 5fc1276dd9f9..c160b42f8b6b 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java @@ -29,6 +29,7 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_ import static android.os.Build.VERSION_CODES.DONUT; import static android.os.Build.VERSION_CODES.O; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.sdk.Flags.majorMinorVersioningScheme; import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; @@ -541,6 +542,7 @@ public class ParsingPackageUtils { pkg.setGwpAsanMode(-1); pkg.setMemtagMode(-1); + pkg.setPageSizeAppCompatFlags(ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED); afterParseBaseApplication(pkg); @@ -1688,6 +1690,21 @@ public class ParsingPackageUtils { targetCode = minCode; } + if (majorMinorVersioningScheme()) { + val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersionFull); + if (val != null) { + if (val.type == TypedValue.TYPE_STRING && val.string != null) { + String minSdkVersionFullString = val.string.toString(); + ParseResult<Void> minSdkVersionFullResult = + FrameworkParsingPackageUtils.verifyMinSdkVersionFull( + minSdkVersionFullString, Build.VERSION.SDK_INT_FULL, input); + if (minSdkVersionFullResult.isError()) { + return input.error(minSdkVersionFullResult); + } + } + } + } + if (isApkInApex) { val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_maxSdkVersion); if (val != null) { @@ -2182,6 +2199,13 @@ public class ParsingPackageUtils { pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1)); pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1)); + + if (Flags.appCompatOption16kb()) { + pkg.setPageSizeAppCompatFlags( + sa.getInt(R.styleable.AndroidManifestApplication_pageSizeCompat, + ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED)); + } + if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) { final boolean v = sa.getBoolean( R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false); diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index 97a2d3beda90..4305ba753e46 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -261,7 +261,7 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements MessagingGroup createdGroup = sInstancePool.acquire(); if (createdGroup == null) { createdGroup = (MessagingGroup) LayoutInflater.from(layout.getContext()).inflate( - R.layout.notification_template_messaging_group, layout, + getMessagingGroupLayoutResource(), layout, false); createdGroup.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); } @@ -269,6 +269,14 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements return createdGroup; } + private static int getMessagingGroupLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_messaging_group; + } else { + return R.layout.notification_template_messaging_group; + } + } + public void removeMessage(MessagingMessage messagingMessage, ArrayList<MessagingLinearLayout.MessagingChild> toRecycle) { View view = messagingMessage.getView(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/Operation.java index 102003e2e371..6f6a0a892964 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operation.java @@ -18,19 +18,19 @@ package com.android.internal.widget.remotecompose.core; import android.annotation.NonNull; /** Base interface for RemoteCompose operations */ -public interface Operation { +public abstract class Operation { /** add the operation to the buffer */ - void write(@NonNull WireBuffer buffer); + public abstract void write(@NonNull WireBuffer buffer); /** * paint an operation * * @param context the paint context used to paint the operation */ - void apply(@NonNull RemoteContext context); + public abstract void apply(@NonNull RemoteContext context); /** Debug utility to display an operation + indentation */ @NonNull - String deepToString(@NonNull String indent); + public abstract String deepToString(@NonNull String indent); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java b/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java new file mode 100644 index 000000000000..741303a40bc9 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java @@ -0,0 +1,36 @@ +/* + * 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.internal.widget.remotecompose.core; + +import android.annotation.NonNull; + +/** Base interface for RemoteCompose operations */ +public interface OperationInterface { + + /** add the operation to the buffer */ + void write(@NonNull WireBuffer buffer); + + /** + * paint an operation + * + * @param context the paint context used to paint the operation + */ + void apply(@NonNull RemoteContext context); + + /** Debug utility to display an operation + indentation */ + @NonNull + String deepToString(@NonNull String indent); +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java index 687a99baff83..006fe3afb4d8 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java @@ -55,7 +55,10 @@ import com.android.internal.widget.remotecompose.core.operations.MatrixSkew; import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate; import com.android.internal.widget.remotecompose.core.operations.NamedVariable; import com.android.internal.widget.remotecompose.core.operations.PaintData; +import com.android.internal.widget.remotecompose.core.operations.PathAppend; +import com.android.internal.widget.remotecompose.core.operations.PathCreate; import com.android.internal.widget.remotecompose.core.operations.PathData; +import com.android.internal.widget.remotecompose.core.operations.PathTween; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; import com.android.internal.widget.remotecompose.core.operations.ShaderData; @@ -178,6 +181,9 @@ public class Operations { public static final int TEXT_MEASURE = 155; public static final int TEXT_LENGTH = 156; public static final int TOUCH_EXPRESSION = 157; + public static final int PATH_TWEEN = 158; + public static final int PATH_CREATE = 159; + public static final int PATH_ADD = 160; ///////////////////////////////////////// ====================== @@ -353,5 +359,8 @@ public class Operations { map.put(TEXT_MEASURE, TextMeasure::read); map.put(TEXT_LENGTH, TextLength::read); map.put(TOUCH_EXPRESSION, TouchExpression::read); + map.put(PATH_TWEEN, PathTween::read); + map.put(PATH_CREATE, PathCreate::read); + map.put(PATH_ADD, PathAppend::read); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java index 38b08e9b0ab3..7ecd11826303 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java @@ -157,6 +157,8 @@ public abstract class PaintContext { public abstract void drawTweenPath( int path1Id, int path2Id, float tween, float start, float stop); + public abstract void tweenPath(int out, int path1, int path2, float tween); + /** * This applies changes to the current paint * diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java index 9999182b53fc..cfdd52212b03 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java @@ -21,7 +21,7 @@ import android.annotation.NonNull; * PaintOperation interface, used for operations aimed at painting (while any operation _can_ paint, * this make it a little more explicit) */ -public abstract class PaintOperation implements Operation { +public abstract class PaintOperation extends Operation { @Override public void apply(@NonNull RemoteContext context) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java index c05079e34505..3a5d68db8a37 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -59,7 +59,10 @@ import com.android.internal.widget.remotecompose.core.operations.MatrixSkew; import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate; import com.android.internal.widget.remotecompose.core.operations.NamedVariable; import com.android.internal.widget.remotecompose.core.operations.PaintData; +import com.android.internal.widget.remotecompose.core.operations.PathAppend; +import com.android.internal.widget.remotecompose.core.operations.PathCreate; import com.android.internal.widget.remotecompose.core.operations.PathData; +import com.android.internal.widget.remotecompose.core.operations.PathTween; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; import com.android.internal.widget.remotecompose.core.operations.TextData; @@ -644,6 +647,37 @@ public class RemoteComposeBuffer { } /** + * interpolate the two paths to produce a 3rd + * + * @param pid1 the first path + * @param pid2 the second path + * @param tween path is the path1+(pat2-path1)*tween + * @return id of the tweened path + */ + public int pathTween(int pid1, int pid2, float tween) { + int out = mRemoteComposeState.nextId(); + PathTween.apply(mBuffer, out, pid1, pid2, tween); + return out; + } + + /** + * Create a path with an initial moveTo + * + * @param x x coordinate of the moveto + * @param y y coordinate of the moveto + * @return id of the created path + */ + public int pathCreate(float x, float y) { + int out = mRemoteComposeState.nextId(); + PathCreate.apply(mBuffer, out, x, y); + return out; + } + + public void pathAppend(int id, float... path) { + PathAppend.apply(mBuffer, id, path); + } + + /** * Draw the specified path * * @param pathId @@ -1154,6 +1188,16 @@ public class RemoteComposeBuffer { } /** + * Reserve a float and returns a NaN number pointing to that float + * + * @return the nan id of float + */ + public float reserveFloatVariable() { + int id = mRemoteComposeState.cacheFloat(0f); + return Utils.asNan(id); + } + + /** * Add a Integer return an id number pointing to that float. * * @param value adds an integer and assigns it an id @@ -1651,8 +1695,8 @@ public class RemoteComposeBuffer { */ public void addModifierScroll(int direction, float positionId, int notches) { // TODO: add support for non-notch behaviors etc. - float max = this.addFloat(0f); - float notchMax = this.addFloat(0f); + float max = this.reserveFloatVariable(); + float notchMax = this.reserveFloatVariable(); float touchExpressionDirection = direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y; this.addTouchExpression( @@ -1807,8 +1851,8 @@ public class RemoteComposeBuffer { ClipRectModifierOperation.apply(mBuffer); } - public void addLoopStart(float count, float from, float step, int indexId) { - LoopOperation.apply(mBuffer, count, from, step, indexId); + public void addLoopStart(int indexId, float from, float step, float until) { + LoopOperation.apply(mBuffer, indexId, from, step, until); } public void addLoopEnd() { diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java index e60695fc4a06..97487e6d2714 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java @@ -15,4 +15,4 @@ */ package com.android.internal.widget.remotecompose.core; -public interface RemoteComposeOperation extends Operation {} +public interface RemoteComposeOperation {} diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java index a903e6ee1f27..f5f9e214723b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java @@ -131,6 +131,16 @@ public class RemoteComposeState implements CollectionsAccess { } } + private final IntMap<float[]> mPathData = new IntMap<>(); + + public void putPathData(int id, float[] data) { + mPathData.put(id, data); + } + + public float[] getPathData(int id) { + return mPathData.get(id); + } + /** * Adds a data Override. * diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java index 26305bf124f7..6eb8463ab308 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java @@ -93,12 +93,20 @@ public abstract class RemoteContext { /** * Load a path under an id. Paths can be use in clip drawPath and drawTweenPath * - * @param instanceId - * @param floatPath + * @param instanceId the id to save this path under + * @param floatPath the path as a float array */ public abstract void loadPathData(int instanceId, @NonNull float[] floatPath); /** + * Load a path under an id. Paths can be use in clip drawPath and drawTweenPath + * + * @param instanceId + * @return the a + */ + public abstract @Nullable float[] getPathData(int instanceId); + + /** * Associate a name with a give id. * * @param varName the name diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java index a64b706fea3b..2f1502cd59c7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java @@ -28,11 +28,17 @@ public class WireBuffer { int mStartingIndex = 0; int mSize = 0; + /** + * Create a wire buffer + * + * @param size the initial size of the buffer + */ public WireBuffer(int size) { mMaxSize = size; mBuffer = new byte[mMaxSize]; } + /** Create a wire buffer of default size */ public WireBuffer() { this(BUFFER_SIZE); } @@ -44,37 +50,74 @@ public class WireBuffer { } } + /** + * get the wire buffer's underlying byte array. Note the array will be bigger that the used + * portion + * + * @return byte array of the wire buffer + */ public @NonNull byte[] getBuffer() { return mBuffer; } + /** + * The current mix size of the buffer + * + * @return max size + */ public int getMax_size() { return mMaxSize; } + /** + * The current point in the buffer which will be written to + * + * @return index pointing into the buffer + */ public int getIndex() { return mIndex; } + /** + * The size of the buffer + * + * @return the size of the buffer + */ public int getSize() { return mSize; } + /** + * Reposition the pointer + * + * @param index the new position of the index + */ public void setIndex(int index) { this.mIndex = index; } + /** + * Write a byte representing the command into the buffer + * + * @param type the command id + */ public void start(int type) { mStartingIndex = mIndex; writeByte(type); } + /** + * Unused Todo remove? + * + * @param type the type of object to write + */ public void startWithSize(int type) { mStartingIndex = mIndex; writeByte(type); mIndex += 4; // skip ahead for the future size } + /** Unused Todo remove? */ public void endWithSize() { int size = mIndex - mStartingIndex; int currentIndex = mIndex; @@ -97,10 +140,20 @@ public class WireBuffer { } } + /** + * return the size of the buffer todo rename to getSize + * + * @return the size of the buffer + */ public int size() { return mSize; } + /** + * Bytes available + * + * @return the size - index + */ public boolean available() { return mSize - mIndex > 0; } @@ -109,28 +162,53 @@ public class WireBuffer { // Read values /////////////////////////////////////////////////////////////////////////// + /** + * read the operation type (reads a single byte) + * + * @return the byte cast to an integer + */ public int readOperationType() { return readByte(); } + /** + * Read a boolean (stored as a byte 1 = true) + * + * @return boolean of the byte + */ public boolean readBoolean() { byte value = mBuffer[mIndex]; mIndex++; return (value == 1); } + /** + * read a single byte byte + * + * @return byte from 0..255 as an Integer + */ public int readByte() { int value = 0xFF & mBuffer[mIndex]; mIndex++; return value; } + /** + * read a short [byte n] << 8 | [byte n+1]; index increast by 2 + * + * @return return a short cast as an integer + */ public int readShort() { int v1 = (mBuffer[mIndex++] & 0xFF) << 8; int v2 = (mBuffer[mIndex++] & 0xFF) << 0; return v1 + v2; } + /** + * Read an integer without incrementing the index + * + * @return the integer + */ public int peekInt() { int tmp = mIndex; int v1 = (mBuffer[tmp++] & 0xFF) << 24; @@ -140,6 +218,11 @@ public class WireBuffer { return v1 + v2 + v3 + v4; } + /** + * Read an integer. index increased by 4 + * + * @return integer + */ public int readInt() { int v1 = (mBuffer[mIndex++] & 0xFF) << 24; int v2 = (mBuffer[mIndex++] & 0xFF) << 16; @@ -148,6 +231,11 @@ public class WireBuffer { return v1 + v2 + v3 + v4; } + /** + * Read a long index is increased by 8 + * + * @return long + */ public long readLong() { long v1 = (mBuffer[mIndex++] & 0xFFL) << 56; long v2 = (mBuffer[mIndex++] & 0xFFL) << 48; @@ -160,14 +248,30 @@ public class WireBuffer { return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8; } + /** + * Read a 32 bit float IEEE standard index is increased by 4 + * + * @return the float + */ public float readFloat() { return java.lang.Float.intBitsToFloat(readInt()); } + /** + * Read a 64 bit double index is increased by 8 + * + * @return double + */ public double readDouble() { return java.lang.Double.longBitsToDouble(readLong()); } + /** + * Read a byte buffer bytes are encoded as 4 byte length followed by length bytes index is + * increased by 4 + number of bytes + * + * @return byte array + */ public @NonNull byte[] readBuffer() { int count = readInt(); byte[] b = Arrays.copyOfRange(mBuffer, mIndex, mIndex + count); @@ -175,6 +279,13 @@ public class WireBuffer { return b; } + /** + * Read a byte buffer limited to max size. bytes are encoded as 4 byte length followed by length + * bytes index is increased by 4 + number of bytes Throw an exception if the read excedes the + * max size. This is the preferred form of read buffer. + * + * @return byte array + */ public @NonNull byte[] readBuffer(int maxSize) { int count = readInt(); if (count < 0 || count > maxSize) { @@ -186,12 +297,23 @@ public class WireBuffer { return b; } + /** + * Read a string encoded in UTF8 The buffer is red with readBuffer and converted to a String + * + * @return unicode string + */ @NonNull public String readUTF8() { byte[] stringBuffer = readBuffer(); return new String(stringBuffer); } + /** + * Read a string encoded in UTF8 The buffer is red with readBuffer and converted to a String + * This is the preferred readUTF8 because it catches errors + * + * @return unicode string + */ @NonNull public String readUTF8(int maxSize) { byte[] stringBuffer = readBuffer(maxSize); @@ -202,18 +324,33 @@ public class WireBuffer { // Write values /////////////////////////////////////////////////////////////////////////// + /** + * Write a boolean value. (written as a byte 1=true) + * + * @param value value to write + */ public void writeBoolean(boolean value) { resize(1); mBuffer[mIndex++] = (byte) (value ? 1 : 0); mSize++; } + /** + * Write a byte value + * + * @param value value to write + */ public void writeByte(int value) { resize(1); mBuffer[mIndex++] = (byte) value; mSize++; } + /** + * Write a short value + * + * @param value value to write + */ public void writeShort(int value) { int need = 2; resize(need); @@ -222,6 +359,11 @@ public class WireBuffer { mSize += need; } + /** + * Write a int (4 byte) value + * + * @param value value to write + */ public void writeInt(int value) { int need = 4; resize(need); @@ -232,6 +374,11 @@ public class WireBuffer { mSize += need; } + /** + * Write a long (8 byte) value + * + * @param value value to write + */ public void writeLong(long value) { int need = 8; resize(need); @@ -246,14 +393,29 @@ public class WireBuffer { mSize += need; } + /** + * Write a 32 bit IEEE float value + * + * @param value value to write + */ public void writeFloat(float value) { writeInt(Float.floatToRawIntBits(value)); } + /** + * Write a 64 bit IEEE double value + * + * @param value value to write + */ public void writeDouble(double value) { writeLong(Double.doubleToRawLongBits(value)); } + /** + * Write a buffer The buffer length is first written followed by the bytes + * + * @param b array of bytes write + */ public void writeBuffer(@NonNull byte[] b) { resize(b.length + 4); writeInt(b.length); @@ -263,6 +425,11 @@ public class WireBuffer { mSize += b.length; } + /** + * Write a string is encoded as UTF8 + * + * @param content the string to write + */ public void writeUTF8(@NonNull String content) { byte[] buffer = content.getBytes(); writeBuffer(buffer); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java index 948007699b80..27ba6528a703 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java @@ -36,7 +36,7 @@ import java.util.List; * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is * compressed and saved in playback the image is decompressed */ -public class BitmapData implements Operation, SerializableToString { +public class BitmapData extends Operation implements SerializableToString { private static final int OP_CODE = Operations.DATA_BITMAP; private static final String CLASS_NAME = "BitmapData"; int mImageId; @@ -85,6 +85,11 @@ public class BitmapData implements Operation, SerializableToString { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -119,6 +124,12 @@ public class BitmapData implements Operation, SerializableToString { buffer.writeBuffer(bitmap); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int imageId = buffer.readInt(); int width = buffer.readInt(); @@ -133,6 +144,11 @@ public class BitmapData implements Operation, SerializableToString { operations.add(new BitmapData(imageId, width, height, bitmap)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Bitmap data") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java index 310b194ec486..5e5e5653053f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java @@ -28,7 +28,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Add a click area to the document */ -public class ClickArea implements RemoteComposeOperation { +public class ClickArea extends Operation implements RemoteComposeOperation { private static final int OP_CODE = Operations.CLICK_AREA; private static final String CLASS_NAME = "ClickArea"; int mId; @@ -118,6 +118,11 @@ public class ClickArea implements RemoteComposeOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -141,6 +146,12 @@ public class ClickArea implements RemoteComposeOperation { buffer.writeInt(metadata); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int contentDescription = buffer.readInt(); @@ -154,6 +165,11 @@ public class ClickArea implements RemoteComposeOperation { operations.add(clickArea); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Define a region you can click on") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java index db93829586bb..2fe56d3ec935 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java @@ -69,6 +69,12 @@ public class ClipPath extends PaintOperation { return "ClipPath " + mId + ";"; } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int pack = buffer.readInt(); int id = pack & 0xFFFFF; @@ -82,6 +88,11 @@ public class ClipPath extends PaintOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -91,6 +102,11 @@ public class ClipPath extends PaintOperation { buffer.writeInt(id); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Intersect the current clip with the path") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java index df54fb1ed834..defa656b44e6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java @@ -31,11 +31,22 @@ public class ClipRect extends DrawBase4 { public static final int OP_CODE = Operations.CLIP_RECT; public static final String CLASS_NAME = "ClipRect"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = ClipRect::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -50,6 +61,11 @@ public class ClipRect extends DrawBase4 { apply(buffer, v1, v2, v3, v4); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("Intersect the current clip with rectangle") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java index 34e93f590dbe..d86576dd99f2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java @@ -29,12 +29,22 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Operation that defines a simple Color based on ID Mainly for colors in theming. */ -public class ColorConstant implements Operation { +public class ColorConstant extends Operation { private static final int OP_CODE = Operations.COLOR_CONSTANT; private static final String CLASS_NAME = "ColorConstant"; + + /** the id of the color */ public int mColorId; + + /** the color value (AARRGGBB) */ public int mColor; + /** + * Creat a color constant + * + * @param colorId id of color + * @param color AARRGGBB value + */ public ColorConstant(int colorId, int color) { this.mColorId = colorId; this.mColor = color; @@ -56,6 +66,11 @@ public class ColorConstant implements Operation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -73,12 +88,23 @@ public class ColorConstant implements Operation { buffer.writeInt(color); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int colorId = buffer.readInt(); int color = buffer.readInt(); operations.add(new ColorConstant(colorId, color)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("Define a Color") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java index c947d1139a2e..66f128f8f478 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java @@ -34,7 +34,7 @@ import java.util.List; * Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID. * mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode */ -public class ColorExpression implements Operation, VariableSupport { +public class ColorExpression extends Operation implements VariableSupport { private static final int OP_CODE = Operations.COLOR_EXPRESSIONS; private static final String CLASS_NAME = "ColorExpression"; public int mId; @@ -204,6 +204,11 @@ public class ColorExpression implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -228,6 +233,12 @@ public class ColorExpression implements Operation, VariableSupport { buffer.writeFloat(tween); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int mode = buffer.readInt(); @@ -238,6 +249,11 @@ public class ColorExpression implements Operation, VariableSupport { operations.add(new ColorExpression(id, mode, color1, color2, tween)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("A Color defined by an expression") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java index b0ccd1871fd2..19c219bc0121 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java @@ -30,7 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; -public class ComponentValue implements Operation, SerializableToString { +public class ComponentValue extends Operation implements SerializableToString { public static final int OP_CODE = Operations.COMPONENT_VALUE; public static final String CLASS_NAME = "ComponentValue"; @@ -41,6 +41,11 @@ public class ComponentValue implements Operation, SerializableToString { private int mComponentID = -1; private int mValueId = -1; + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -78,6 +83,12 @@ public class ComponentValue implements Operation, SerializableToString { // Nothing } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int type = buffer.readInt(); int componentId = buffer.readInt(); @@ -86,6 +97,11 @@ public class ComponentValue implements Operation, SerializableToString { operations.add(op); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("Encode a component-related value (eg its width, height etc.)") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java index bfaf139da21b..ac6271c6328e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java @@ -32,7 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Array import java.util.Arrays; import java.util.List; -public class DataListFloat implements VariableSupport, ArrayAccess, Operation { +public class DataListFloat extends Operation implements VariableSupport, ArrayAccess { private static final int OP_CODE = Operations.FLOAT_LIST; private static final String CLASS_NAME = "IdListData"; private final int mId; @@ -79,6 +79,12 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation { } } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int len = buffer.readInt(); @@ -93,6 +99,11 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation { operations.add(data); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("a list of Floats") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java index 9b286b94d553..47cbff36d492 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java @@ -33,7 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Array import java.util.Arrays; import java.util.List; -public class DataListIds implements VariableSupport, ArrayAccess, Operation { +public class DataListIds extends Operation implements VariableSupport, ArrayAccess { private static final int OP_CODE = Operations.ID_LIST; private static final String CLASS_NAME = "IdListData"; private final int mId; @@ -71,6 +71,12 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation { } } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int len = buffer.readInt(); @@ -85,6 +91,11 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation { operations.add(data); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("a list of id's") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java index 643afc85e69f..e888074cda74 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java @@ -31,7 +31,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.DataM import java.util.List; /** This is a map of strings to type & Id */ -public class DataMapIds implements Operation { +public class DataMapIds extends Operation { private static final int OP_CODE = Operations.ID_MAP; private static final String CLASS_NAME = "DataMapIds"; int mId; @@ -105,6 +105,12 @@ public class DataMapIds implements Operation { } } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int len = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java index eae532c14344..9af2343a5fa4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java @@ -31,7 +31,7 @@ import com.android.internal.widget.remotecompose.core.types.LongConstant; import java.util.List; /** This can lookup in a map given a string writing the results to an id. */ -public class DataMapLookup implements Operation { +public class DataMapLookup extends Operation { private static final int OP_CODE = Operations.DATA_MAP_LOOKUP; private static final String CLASS_NAME = "DataMapLookup"; public int mId; @@ -109,12 +109,17 @@ public class DataMapLookup implements Operation { operations.add(new DataMapLookup(id, mapId, stringId)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) - .description("A float and its associated id") + .description("Look up a value in a data map") .field(INT, "id", "id of float") .field(INT, "dataMapId", "32-bit float value") - .field(INT, "value", "32-bit float value"); + .field(INT, "stringId", "32-bit float value"); } @Override diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java index 629f78647079..3f95f02747f9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java @@ -30,11 +30,22 @@ public class DrawArc extends DrawBase6 { public static final int OP_CODE = Operations.DRAW_ARC; private static final String CLASS_NAME = "DrawArc"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = DrawArc::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -79,6 +90,11 @@ public class DrawArc extends DrawBase6 { apply(buffer, v1, v2, v3, v4, v5, v6); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java index 9c23c9559953..69f5cc52a78a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java @@ -100,15 +100,21 @@ public class DrawBitmap extends PaintOperation implements VariableSupport { + ";"; } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); float sLeft = buffer.readFloat(); float srcTop = buffer.readFloat(); float srcRight = buffer.readFloat(); float srcBottom = buffer.readFloat(); - int discriptionId = buffer.readInt(); + int descriptionId = buffer.readInt(); - DrawBitmap op = new DrawBitmap(id, sLeft, srcTop, srcRight, srcBottom, discriptionId); + DrawBitmap op = new DrawBitmap(id, sLeft, srcTop, srcRight, srcBottom, descriptionId); operations.add(op); } @@ -117,6 +123,11 @@ public class DrawBitmap extends PaintOperation implements VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -138,6 +149,11 @@ public class DrawBitmap extends PaintOperation implements VariableSupport { buffer.writeInt(descriptionId); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", OP_CODE, CLASS_NAME) .description("Draw a bitmap") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java index da9fe247bced..66646d7c2faa 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java @@ -111,6 +111,11 @@ public class DrawBitmapInt extends PaintOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -140,6 +145,12 @@ public class DrawBitmapInt extends PaintOperation { buffer.writeInt(cdId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int imageId = buffer.readInt(); int sLeft = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java index e9f81d52d67f..170148608ab3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java @@ -196,6 +196,11 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -232,6 +237,12 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport buffer.writeInt(cdId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int imageId = buffer.readInt(); @@ -265,6 +276,11 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport operations.add(op); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", OP_CODE, CLASS_NAME) .description("Draw a bitmap using integer coordinates") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java index 11bd49a4a651..e6aecdbf8bbe 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java @@ -30,11 +30,22 @@ public class DrawCircle extends DrawBase3 { private static final int OP_CODE = Operations.DRAW_CIRCLE; private static final String CLASS_NAME = "DrawCircle"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = DrawCircle::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -44,6 +55,11 @@ public class DrawCircle extends DrawBase3 { return CLASS_NAME; } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Draw a Circle") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java index 7310a9defba6..04f32642d5fa 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java @@ -32,11 +32,22 @@ public class DrawLine extends DrawBase4 implements SerializableToString { private static final int OP_CODE = Operations.DRAW_LINE; private static final String CLASS_NAME = "DrawLine"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = DrawLine::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -46,6 +57,11 @@ public class DrawLine extends DrawBase4 implements SerializableToString { return CLASS_NAME; } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Draw a line segment") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java index aa5116e950c5..0a50042b11c7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java @@ -30,11 +30,22 @@ public class DrawOval extends DrawBase4 { private static final int OP_CODE = Operations.DRAW_OVAL; private static final String CLASS_NAME = "DrawOval"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = DrawOval::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -44,6 +55,11 @@ public class DrawOval extends DrawBase4 { return CLASS_NAME; } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Draw the specified oval") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java index d35094b0b351..41b8243f070f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java @@ -50,6 +50,12 @@ public class DrawPath extends PaintOperation { return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd; } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); DrawPath op = new DrawPath(id); @@ -61,6 +67,11 @@ public class DrawPath extends PaintOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.DRAW_PATH; } @@ -70,6 +81,11 @@ public class DrawPath extends PaintOperation { buffer.writeInt(id); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", OP_CODE, CLASS_NAME) .description("Draw a bitmap using integer coordinates") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java index db7633cbe335..7e22550d6544 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java @@ -31,11 +31,22 @@ public class DrawRect extends DrawBase4 { private static final int OP_CODE = Operations.DRAW_RECT; private static final String CLASS_NAME = "DrawRect"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = DrawRect::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -45,6 +56,11 @@ public class DrawRect extends DrawBase4 { return CLASS_NAME; } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Draw the specified rectangle") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java index c306e2b5f041..a41e46e03506 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java @@ -31,11 +31,22 @@ public class DrawRoundRect extends DrawBase6 { private static final int OP_CODE = Operations.DRAW_ROUND_RECT; private static final String CLASS_NAME = "DrawRoundRect"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = DrawRoundRect::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -80,6 +91,11 @@ public class DrawRoundRect extends DrawBase6 { apply(buffer, v1, v2, v3, v4, v5, v6); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Draw the specified round-rect") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java index 3b60df7d529e..7616df09b6cc 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java @@ -30,11 +30,22 @@ public class DrawSector extends DrawBase6 { public static final int OP_CODE = Operations.DRAW_SECTOR; private static final String CLASS_NAME = "DrawSector"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = DrawSector::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -79,6 +90,11 @@ public class DrawSector extends DrawBase6 { apply(buffer, v1, v2, v3, v4, v5, v6); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java index 9c587aba3f7b..2c5d790b2f2a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java @@ -101,6 +101,12 @@ public class DrawText extends PaintOperation implements VariableSupport { + floatToString(mY, mOutY); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int text = buffer.readInt(); int start = buffer.readInt(); @@ -120,6 +126,11 @@ public class DrawText extends PaintOperation implements VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -158,6 +169,11 @@ public class DrawText extends PaintOperation implements VariableSupport { buffer.writeBoolean(rtl); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", id(), CLASS_NAME) .description("Draw a run of text, all in a single direction") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java index 8b7018191f4d..7de52b8e5f3e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java @@ -111,6 +111,12 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport return Float.toString(v); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textID = buffer.readInt(); float x = buffer.readFloat(); @@ -129,6 +135,11 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -161,6 +172,11 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport buffer.writeInt(flags); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", OP_CODE, CLASS_NAME) .description("Draw text centered about an anchor point") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java index e90122bb95ac..18d9fdf1b671 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java @@ -83,6 +83,12 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport { + Utils.floatToString(mVOffset, mOutVOffset); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); int pathId = buffer.readInt(); @@ -97,6 +103,11 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport { return "DrawTextOnPath"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.DRAW_TEXT_ON_PATH; } @@ -110,6 +121,11 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport { buffer.writeFloat(hOffset); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", OP_CODE, CLASS_NAME) .description("Draw text along path object") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java index 0aaaf42ba838..b83e4c2191b2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java @@ -92,6 +92,12 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport { + floatToString(mStop, mOutStop); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int path1Id = buffer.readInt(); int path2Id = buffer.readInt(); @@ -107,6 +113,11 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport { return "DrawTweenPath"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.DRAW_TWEEN_PATH; } @@ -126,6 +137,11 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport { buffer.writeFloat(stop); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", OP_CODE, CLASS_NAME) .description("Draw text along path object") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java index 17aaf3bb9cd8..7dd435a5c5b1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java @@ -29,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Operation to deal with Text data */ -public class FloatConstant implements com.android.internal.widget.remotecompose.core.Operation { +public class FloatConstant extends Operation { private static final int OP_CODE = Operations.DATA_FLOAT; private static final String CLASS_NAME = "FloatConstant"; public int mTextId; @@ -56,6 +56,11 @@ public class FloatConstant implements com.android.internal.widget.remotecompose. return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -73,6 +78,12 @@ public class FloatConstant implements com.android.internal.widget.remotecompose. buffer.writeFloat(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); @@ -80,6 +91,11 @@ public class FloatConstant implements com.android.internal.widget.remotecompose. operations.add(new FloatConstant(textId, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("A float and its associated id") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java index eef9746ffaf6..3d92e129a236 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java @@ -42,7 +42,7 @@ import java.util.List; * like injecting the width of the component int draw rect As well as supporting generalized * animation floats. The floats represent a RPN style calculator */ -public class FloatExpression implements Operation, VariableSupport { +public class FloatExpression extends Operation implements VariableSupport { private static final int OP_CODE = Operations.ANIMATED_FLOAT; private static final String CLASS_NAME = "FloatExpression"; public int mId; @@ -146,16 +146,19 @@ public class FloatExpression implements Operation, VariableSupport { if (Float.isNaN(mLastChange)) { mLastChange = t; } - if (mFloatAnimation != null) { + if (mFloatAnimation != null && !Float.isNaN(mLastCalculatedValue)) { float f = mFloatAnimation.get(t - mLastChange); context.loadFloat(mId, f); } else if (mSpring != null) { float f = mSpring.get(t - mLastChange); context.loadFloat(mId, f); } else { - context.loadFloat( - mId, - mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length)); + float v = + mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length); + if (mFloatAnimation != null) { + mFloatAnimation.setTargetValue(v); + } + context.loadFloat(mId, v); } } @@ -207,6 +210,11 @@ public class FloatExpression implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -243,12 +251,18 @@ public class FloatExpression implements Operation, VariableSupport { } } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int len = buffer.readInt(); int valueLen = len & 0xFFFF; if (valueLen > MAX_EXPRESSION_SIZE) { - throw new RuntimeException("Float expression to long"); + throw new RuntimeException("Float expression too long"); } int animLen = (len >> 16) & 0xFFFF; float[] values = new float[valueLen]; @@ -268,6 +282,11 @@ public class FloatExpression implements Operation, VariableSupport { operations.add(new FloatExpression(id, values, animation)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("A Float expression") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java index 009aa0339625..04e4346cf05d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java @@ -35,7 +35,7 @@ import java.util.List; * <p>It encodes the version of the document (following semantic versioning) as well as the * dimensions of the document in pixels. */ -public class Header implements RemoteComposeOperation { +public class Header extends Operation implements RemoteComposeOperation { private static final int OP_CODE = Operations.HEADER; private static final String CLASS_NAME = "Header"; public static final int MAJOR_VERSION = 0; @@ -120,6 +120,11 @@ public class Header implements RemoteComposeOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -136,6 +141,12 @@ public class Header implements RemoteComposeOperation { buffer.writeLong(capabilities); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int majorVersion = buffer.readInt(); int minorVersion = buffer.readInt(); @@ -157,6 +168,11 @@ public class Header implements RemoteComposeOperation { operations.add(header); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Protocol Operations", OP_CODE, CLASS_NAME) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java index c49f74de7893..67274af7c283 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java @@ -38,7 +38,7 @@ import java.util.List; * like injecting the width of the component int draw rect As well as supporting generalized * animation floats. The floats represent a RPN style calculator */ -public class IntegerExpression implements Operation, VariableSupport { +public class IntegerExpression extends Operation implements VariableSupport { private static final int OP_CODE = Operations.INTEGER_EXPRESSION; private static final String CLASS_NAME = "IntegerExpression"; public int mId; @@ -141,6 +141,11 @@ public class IntegerExpression implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -163,6 +168,12 @@ public class IntegerExpression implements Operation, VariableSupport { } } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int mask = buffer.readInt(); @@ -178,6 +189,11 @@ public class IntegerExpression implements Operation, VariableSupport { operations.add(new IntegerExpression(id, mask, values)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Expression that computes an integer") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java index 4e7ee4d135ee..aed597ae7494 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java @@ -37,6 +37,12 @@ public class MatrixRestore extends PaintOperation { apply(buffer); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { MatrixRestore op = new MatrixRestore(); operations.add(op); @@ -53,6 +59,11 @@ public class MatrixRestore extends PaintOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -61,6 +72,11 @@ public class MatrixRestore extends PaintOperation { buffer.start(OP_CODE); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Restore the matrix and clip"); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java index 438a2aad648a..fece143ebfa1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java @@ -30,6 +30,12 @@ public class MatrixRotate extends DrawBase3 { public static final int OP_CODE = Operations.MATRIX_ROTATE; private static final String CLASS_NAME = "MatrixRotate"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = new Maker() { @@ -42,6 +48,11 @@ public class MatrixRotate extends DrawBase3 { read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -51,6 +62,11 @@ public class MatrixRotate extends DrawBase3 { return CLASS_NAME; } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("apply rotation to matrix") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java index 09f54a52e6a3..7eb7b3ffaf34 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java @@ -41,6 +41,12 @@ public class MatrixSave extends PaintOperation { return "MatrixSave;"; } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { MatrixSave op = new MatrixSave(); operations.add(op); @@ -51,6 +57,11 @@ public class MatrixSave extends PaintOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -59,6 +70,11 @@ public class MatrixSave extends PaintOperation { buffer.start(Operations.MATRIX_SAVE); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Save the matrix and clip to a stack"); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java index 630458499977..49bdd1b06eed 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java @@ -30,11 +30,22 @@ public class MatrixScale extends DrawBase4 { public static final int OP_CODE = Operations.MATRIX_SCALE; public static final String CLASS_NAME = "MatrixScale"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = MatrixScale::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -49,9 +60,14 @@ public class MatrixScale extends DrawBase4 { apply(buffer, v1, v2, v3, v4); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) - .description("Draw the specified Oval") + .description("Scale the following draw commands") .field(DocumentedOperation.FLOAT, "scaleX", "The amount to scale in X") .field(DocumentedOperation.FLOAT, "scaleY", "The amount to scale in Y") .field(DocumentedOperation.FLOAT, "pivotX", "The x-coordinate for the pivot point") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java index 675cf0de4743..54b6fd1fa9ae 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java @@ -31,11 +31,22 @@ public class MatrixSkew extends DrawBase2 { public static final int OP_CODE = Operations.MATRIX_SKEW; public static final String CLASS_NAME = "MatrixSkew"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = MatrixSkew::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -50,6 +61,11 @@ public class MatrixSkew extends DrawBase2 { apply(buffer, v1, v2); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Current matrix with the specified skew.") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java index b0a7d352dfe3..b57d83b770b2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java @@ -30,11 +30,22 @@ public class MatrixTranslate extends DrawBase2 { public static final int OP_CODE = Operations.MATRIX_TRANSLATE; public static final String CLASS_NAME = "MatrixTranslate"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = MatrixTranslate::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -49,6 +60,11 @@ public class MatrixTranslate extends DrawBase2 { apply(buffer, v1, v2); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, "MatrixTranslate") .description("Preconcat the current matrix with the specified translation") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java index 5ca91af91f86..3c82f2b27ca6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java @@ -30,7 +30,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Operation to deal with Text data */ -public class NamedVariable implements Operation { +public class NamedVariable extends Operation { private static final int OP_CODE = Operations.NAMED_VARIABLE; private static final String CLASS_NAME = "NamedVariable"; public final int mVarId; @@ -69,6 +69,11 @@ public class NamedVariable implements Operation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -89,6 +94,12 @@ public class NamedVariable implements Operation { buffer.writeUTF8(text); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int varId = buffer.readInt(); int varType = buffer.readInt(); @@ -96,6 +107,11 @@ public class NamedVariable implements Operation { operations.add(new NamedVariable(varId, varType, text)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Add a string name for an ID") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java index b24df8a6a91e..3c0a842371c7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java @@ -66,6 +66,11 @@ public class PaintData extends PaintOperation implements VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -75,6 +80,12 @@ public class PaintData extends PaintOperation implements VariableSupport { paintBundle.writeBundle(buffer); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { PaintData data = new PaintData(); data.mPaintData.readBundle(buffer); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java new file mode 100644 index 000000000000..2b00001a521e --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY; +import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; + +import java.util.Arrays; +import java.util.List; + +public class PathAppend extends PaintOperation implements VariableSupport { + private static final int OP_CODE = Operations.PATH_ADD; + private static final String CLASS_NAME = "PathAppend"; + int mInstanceId; + float[] mFloatPath; + float[] mOutputPath; + + PathAppend(int instanceId, float[] floatPath) { + mInstanceId = instanceId; + mFloatPath = floatPath; + mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length); + } + + @Override + public void updateVariables(@NonNull RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + float v = mFloatPath[i]; + if (Utils.isVariable(v)) { + mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v; + } else { + mOutputPath[i] = v; + } + } + } + + @Override + public void registerListening(@NonNull RemoteContext context) { + for (float v : mFloatPath) { + if (Float.isNaN(v)) { + context.listensTo(Utils.idFromNan(v), this); + } + } + } + + @Override + public void write(@NonNull WireBuffer buffer) { + apply(buffer, mInstanceId, mOutputPath); + } + + @NonNull + @Override + public String deepToString(String indent) { + return PathData.pathString(mFloatPath); + } + + @NonNull + @Override + public String toString() { + return "PathAppend[" + mInstanceId + "] += " + "\"" + pathString(mOutputPath) + "\""; + } + + /** + * public float[] getFloatPath(PaintContext context) { float[] ret = mRetFloats; // Assume + * retFloats is declared elsewhere if (ret == null) { return mFloatPath; // Assume floatPath is + * declared elsewhere } float[] localRef = mRef; // Assume ref is of type Float[] if (localRef + * == null) { for (int i = 0; i < mFloatPath.length; i++) { ret[i] = mFloatPath[i]; } } else { + * for (int i = 0; i < mFloatPath.length; i++) { float lr = localRef[i]; if (Float.isNaN(lr)) { + * ret[i] = Utils.getActualValue(lr); } else { ret[i] = mFloatPath[i]; } } } return ret; } + */ + public static final int MOVE = 10; + + public static final int LINE = 11; + public static final int QUADRATIC = 12; + public static final int CONIC = 13; + public static final int CUBIC = 14; + public static final int CLOSE = 15; + public static final int DONE = 16; + public static final float MOVE_NAN = Utils.asNan(MOVE); + public static final float LINE_NAN = Utils.asNan(LINE); + public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC); + public static final float CONIC_NAN = Utils.asNan(CONIC); + public static final float CUBIC_NAN = Utils.asNan(CUBIC); + public static final float CLOSE_NAN = Utils.asNan(CLOSE); + public static final float DONE_NAN = Utils.asNan(DONE); + + @NonNull + public static String name() { + return CLASS_NAME; + } + + /** + * The OP_CODE for this command + * + * @return the opcode + */ + public static int id() { + return OP_CODE; + } + + public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] data) { + buffer.start(OP_CODE); + buffer.writeInt(id); + buffer.writeInt(data.length); + for (float datum : data) { + buffer.writeFloat(datum); + } + } + + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ + public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { + int id = buffer.readInt(); + int len = buffer.readInt(); + float[] data = new float[len]; + for (int i = 0; i < data.length; i++) { + data[i] = buffer.readFloat(); + } + operations.add(new PathAppend(id, data)); + } + + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ + public static void documentation(@NonNull DocumentationBuilder doc) { + doc.operation("Data Operations", OP_CODE, CLASS_NAME) + .description("Append to a Path") + .field(DocumentedOperation.INT, "id", "id string") + .field(INT, "length", "id string") + .field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats"); + } + + @Override + public void paint(PaintContext context) {} + + @Override + public void apply(@NonNull RemoteContext context) { + updateVariables(context); + float[] data = context.getPathData(mInstanceId); + float[] out = mOutputPath; + if (data != null) { + out = new float[data.length + mOutputPath.length]; + + for (int i = 0; i < data.length; i++) { + out[i] = data[i]; + } + for (int i = 0; i < mOutputPath.length; i++) { + out[i + data.length] = mOutputPath[i]; + } + } else { + System.out.println(">>>>>>>>>>> DATA IS NULL"); + } + context.loadPathData(mInstanceId, out); + } + + @NonNull + public static String pathString(@Nullable float[] path) { + if (path == null) { + return "null"; + } + StringBuilder str = new StringBuilder(); + for (int i = 0; i < path.length; i++) { + if (Float.isNaN(path[i])) { + int id = Utils.idFromNan(path[i]); // Assume idFromNan is defined elsewhere + if (id <= DONE) { // Assume DONE is a constant + switch (id) { + case MOVE: + str.append("M"); + break; + case LINE: + str.append("L"); + break; + case QUADRATIC: + str.append("Q"); + break; + case CONIC: + str.append("R"); + break; + case CUBIC: + str.append("C"); + break; + case CLOSE: + str.append("Z"); + break; + case DONE: + str.append("."); + break; + default: + str.append("[" + id + "]"); + break; + } + } else { + str.append("(" + id + ")"); + } + } + } + return str.toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java new file mode 100644 index 000000000000..b62f36b8db5f --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; + +import java.util.Arrays; +import java.util.List; + +public class PathCreate extends PaintOperation implements VariableSupport { + private static final int OP_CODE = Operations.PATH_CREATE; + private static final String CLASS_NAME = "PathCreate"; + int mInstanceId; + float[] mFloatPath; + float[] mOutputPath; + + PathCreate(int instanceId, float startX, float startY) { + mInstanceId = instanceId; + mFloatPath = new float[] {PathData.MOVE_NAN, startX, startY}; + mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length); + } + + @Override + public void updateVariables(@NonNull RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + float v = mFloatPath[i]; + if (Utils.isVariable(v)) { + mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v; + } else { + mOutputPath[i] = v; + } + } + } + + @Override + public void registerListening(@NonNull RemoteContext context) { + for (float v : mFloatPath) { + if (Float.isNaN(v)) { + context.listensTo(Utils.idFromNan(v), this); + } + } + } + + @Override + public void write(@NonNull WireBuffer buffer) { + apply(buffer, mInstanceId, mFloatPath[1], mFloatPath[2]); + } + + @NonNull + @Override + public String deepToString(String indent) { + return pathString(mFloatPath); + } + + @NonNull + @Override + public String toString() { + return "PathCreate[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\""; + } + + public static final int MOVE = 10; + public static final int LINE = 11; + public static final int QUADRATIC = 12; + public static final int CONIC = 13; + public static final int CUBIC = 14; + public static final int CLOSE = 15; + public static final int DONE = 16; + public static final float MOVE_NAN = Utils.asNan(MOVE); + public static final float LINE_NAN = Utils.asNan(LINE); + public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC); + public static final float CONIC_NAN = Utils.asNan(CONIC); + public static final float CUBIC_NAN = Utils.asNan(CUBIC); + public static final float CLOSE_NAN = Utils.asNan(CLOSE); + public static final float DONE_NAN = Utils.asNan(DONE); + + @NonNull + public static String name() { + return CLASS_NAME; + } + + /** + * The OP_CODE for this command + * + * @return the opcode + */ + public static int id() { + return OP_CODE; + } + + public static void apply(@NonNull WireBuffer buffer, int id, float startX, float startY) { + buffer.start(OP_CODE); + buffer.writeInt(id); + buffer.writeFloat(startX); + buffer.writeFloat(startY); + } + + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ + public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { + + int id = buffer.readInt(); + float startX = buffer.readFloat(); + float startY = buffer.readFloat(); + operations.add(new PathCreate(id, startX, startY)); + } + + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ + public static void documentation(@NonNull DocumentationBuilder doc) { + doc.operation("Data Operations", OP_CODE, CLASS_NAME) + .description("Encode a Path ") + .field(DocumentedOperation.INT, "id", "id of path") + .field(FLOAT, "startX", "initial start x") + .field(FLOAT, "startX", "initial start y"); + } + + @NonNull + public static String pathString(@Nullable float[] path) { + if (path == null) { + return "null"; + } + StringBuilder str = new StringBuilder(); + for (int i = 0; i < path.length; i++) { + if (i != 0) { + str.append(" "); + } + if (Float.isNaN(path[i])) { + int id = Utils.idFromNan(path[i]); // Assume idFromNan is defined elsewhere + if (id <= DONE) { // Assume DONE is a constant + switch (id) { + case MOVE: + str.append("M"); + break; + case LINE: + str.append("L"); + break; + case QUADRATIC: + str.append("Q"); + break; + case CONIC: + str.append("R"); + break; + case CUBIC: + str.append("C"); + break; + case CLOSE: + str.append("Z"); + break; + case DONE: + str.append("."); + break; + default: + str.append("[" + id + "]"); + break; + } + } else { + str.append("(" + id + ")"); + } + } else { + str.append(path[i]); + } + } + return str.toString(); + } + + @Override + public void paint(PaintContext context) {} + + @Override + public void apply(@NonNull RemoteContext context) { + context.loadPathData(mInstanceId, mOutputPath); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java index 509f362c02ed..4ec5436c8689 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java @@ -32,7 +32,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.Arrays; import java.util.List; -public class PathData implements Operation, VariableSupport { +public class PathData extends Operation implements VariableSupport { private static final int OP_CODE = Operations.DATA_PATH; private static final String CLASS_NAME = "PathData"; int mInstanceId; @@ -112,6 +112,11 @@ public class PathData implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -125,6 +130,12 @@ public class PathData implements Operation, VariableSupport { } } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int imageId = buffer.readInt(); int len = buffer.readInt(); @@ -135,6 +146,11 @@ public class PathData implements Operation, VariableSupport { operations.add(new PathData(imageId, data)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Encode a Path ") @@ -143,6 +159,12 @@ public class PathData implements Operation, VariableSupport { .field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats"); } + /** + * Render a path as a string + * + * @param path path as a array of floats + * @return string describing the path + */ @NonNull public static String pathString(@Nullable float[] path) { if (path == null) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java new file mode 100644 index 000000000000..a6fa680f647a --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java @@ -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.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT; +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import android.annotation.NonNull; + +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; + +import java.util.List; + +/** Operation to deal with Path data */ +public class PathTween extends PaintOperation implements VariableSupport { + private static final int OP_CODE = Operations.PATH_TWEEN; + private static final String CLASS_NAME = "PathTween"; + public int mOutId; + public int mPathId1; + public int mPathId2; + public float mTween; + public float mTweenOut; + + public PathTween(int outId, int pathId1, int pathId2, float tween) { + this.mOutId = outId; + this.mPathId1 = pathId1; + this.mPathId2 = pathId2; + this.mTween = tween; + this.mTweenOut = mTween; + } + + @Override + public void updateVariables(@NonNull RemoteContext context) { + mTweenOut = Float.isNaN(mTween) ? context.getFloat(Utils.idFromNan(mTween)) : mTween; + } + + @Override + public void registerListening(@NonNull RemoteContext context) { + if (Float.isNaN(mTween)) { + context.listensTo(Utils.idFromNan(mTween), this); + } + } + + @Override + public void write(@NonNull WireBuffer buffer) { + apply(buffer, mOutId, mPathId1, mPathId2, mTween); + } + + @NonNull + @Override + public String toString() { + return "PathTween[" + + mOutId + + "] = [" + + mPathId1 + + " ] + [ " + + mPathId2 + + "], " + + floatToString(mTween, mTweenOut); + } + + @NonNull + public static String name() { + return CLASS_NAME; + } + + /** + * The OP_CODE for this command + * + * @return the opcode + */ + public static int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * + * @param buffer buffer to write to + * @param outId id of the path + * @param pathId1 source path 1 + * @param pathId2 source path 2 + * @param tween interpolate between two paths + */ + public static void apply( + @NonNull WireBuffer buffer, int outId, int pathId1, int pathId2, float tween) { + buffer.start(OP_CODE); + buffer.writeInt(outId); + buffer.writeInt(pathId1); + buffer.writeInt(pathId2); + buffer.writeFloat(tween); + } + + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ + public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { + int outId1 = buffer.readInt(); + int pathId1 = buffer.readInt(); + int pathId2 = buffer.readInt(); + float tween = buffer.readFloat(); + + operations.add(new PathTween(outId1, pathId1, pathId2, tween)); + } + + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ + public static void documentation(@NonNull DocumentationBuilder doc) { + doc.operation("Data Operations", OP_CODE, CLASS_NAME) + .description("Merge two string into one") + .field(DocumentedOperation.INT, "pathId", "id of the path") + .field(INT, "srcPathId1", "id of the path") + .field(INT, "srcPathId1", "x Shift of the path"); + } + + @NonNull + @Override + public String deepToString(String indent) { + return indent + toString(); + } + + @Override + public void paint(PaintContext context) { + context.tweenPath(mOutId, mPathId1, mPathId2, mTweenOut); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java index 849412618c2c..aaa717629c2e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java @@ -33,7 +33,7 @@ import java.util.List; * <p>It encodes the version of the document (following semantic versioning) as well as the * dimensions of the document in pixels. */ -public class RootContentBehavior implements RemoteComposeOperation { +public class RootContentBehavior extends Operation implements RemoteComposeOperation { private static final int OP_CODE = Operations.ROOT_CONTENT_BEHAVIOR; private static final String CLASS_NAME = "RootContentBehavior"; int mScroll = NONE; @@ -205,6 +205,11 @@ public class RootContentBehavior implements RemoteComposeOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -218,6 +223,12 @@ public class RootContentBehavior implements RemoteComposeOperation { buffer.writeInt(mode); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int scroll = buffer.readInt(); int alignment = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java index 109945f21ec5..e92daa384dc3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java @@ -28,7 +28,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Describe a content description for the document */ -public class RootContentDescription implements RemoteComposeOperation { +public class RootContentDescription extends Operation implements RemoteComposeOperation { private static final int OP_CODE = Operations.ROOT_CONTENT_DESCRIPTION; private static final String CLASS_NAME = "RootContentDescription"; int mContentDescription; @@ -69,6 +69,11 @@ public class RootContentDescription implements RemoteComposeOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -78,12 +83,23 @@ public class RootContentDescription implements RemoteComposeOperation { buffer.writeInt(contentDescription); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int contentDescription = buffer.readInt(); RootContentDescription header = new RootContentDescription(contentDescription); operations.add(header); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Protocol Operations", OP_CODE, CLASS_NAME) .description("Content description of root") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java index e967ff4f4ff4..e2502feb2bb1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java @@ -41,7 +41,7 @@ import java.util.List; * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is * compressed and saved in playback the image is decompressed */ -public class ShaderData implements Operation, VariableSupport { +public class ShaderData extends Operation implements VariableSupport { private static final int OP_CODE = Operations.DATA_SHADER; private static final String CLASS_NAME = "ShaderData"; int mShaderTextId; // the actual text of a shader @@ -203,6 +203,11 @@ public class ShaderData implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -266,6 +271,12 @@ public class ShaderData implements Operation, VariableSupport { } } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int shaderID = buffer.readInt(); int shaderTextId = buffer.readInt(); @@ -318,6 +329,11 @@ public class ShaderData implements Operation, VariableSupport { operations.add(new ShaderData(shaderID, shaderTextId, floatMap, intMap, bitmapMap)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Shader") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java index ade008e3bdb4..3f679bf47582 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java @@ -31,7 +31,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Operation to deal with Text data */ -public class TextData implements Operation, SerializableToString { +public class TextData extends Operation implements SerializableToString { private static final int OP_CODE = Operations.DATA_TEXT; private static final String CLASS_NAME = "TextData"; public final int mTextId; @@ -59,6 +59,11 @@ public class TextData implements Operation, SerializableToString { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -69,6 +74,12 @@ public class TextData implements Operation, SerializableToString { buffer.writeUTF8(text); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); @@ -76,6 +87,11 @@ public class TextData implements Operation, SerializableToString { operations.add(new TextData(textId, text)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Encode a string ") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java index 865ee81f2725..4d01e0c3cbe4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java @@ -36,7 +36,7 @@ import java.util.List; * [command][textID][before,after][flags] before and after define number of digits before and after * the decimal point */ -public class TextFromFloat implements Operation, VariableSupport { +public class TextFromFloat extends Operation implements VariableSupport { private static final int OP_CODE = Operations.TEXT_FROM_FLOAT; private static final String CLASS_NAME = "TextFromFloat"; public int mTextId; @@ -127,6 +127,11 @@ public class TextFromFloat implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -155,6 +160,12 @@ public class TextFromFloat implements Operation, VariableSupport { buffer.writeInt(flags); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); float value = buffer.readFloat(); @@ -166,6 +177,11 @@ public class TextFromFloat implements Operation, VariableSupport { operations.add(new TextFromFloat(textId, value, pre, post, flags)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("Draw text along path object") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java index 6ff687b7494e..37ea567f5913 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java @@ -28,7 +28,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; /** Operation to measure the length of the text */ -public class TextLength implements Operation { +public class TextLength extends Operation { private static final int OP_CODE = Operations.TEXT_LENGTH; private static final String CLASS_NAME = "TextLength"; public int mLengthId; @@ -54,6 +54,11 @@ public class TextLength implements Operation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -71,12 +76,23 @@ public class TextLength implements Operation { buffer.writeInt(textId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int lengthId = buffer.readInt(); int textId = buffer.readInt(); operations.add(new TextLength(lengthId, textId)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("get the length of the text and store in float table") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java index cc812a8e160e..3ec6f019c358 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java @@ -34,7 +34,7 @@ import java.util.List; * [command][textID][before,after][flags] before and after define number of digits before and after * the decimal point */ -public class TextLookup implements Operation, VariableSupport { +public class TextLookup extends Operation implements VariableSupport { private static final int OP_CODE = Operations.TEXT_LOOKUP; private static final String CLASS_NAME = "TextFromFloat"; public int mTextId; @@ -84,6 +84,11 @@ public class TextLookup implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -103,6 +108,12 @@ public class TextLookup implements Operation, VariableSupport { buffer.writeFloat(index); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); int dataSetId = buffer.readInt(); @@ -110,6 +121,11 @@ public class TextLookup implements Operation, VariableSupport { operations.add(new TextLookup(textId, dataSetId, index)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("Look an array and turn into a text object") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java index 74be698872fa..9c0ee535f62a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java @@ -30,7 +30,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Operation convert int index of a list to text */ -public class TextLookupInt implements Operation, VariableSupport { +public class TextLookupInt extends Operation implements VariableSupport { private static final int OP_CODE = Operations.TEXT_LOOKUP_INT; private static final String CLASS_NAME = "TextFromINT"; public int mTextId; @@ -77,6 +77,11 @@ public class TextLookupInt implements Operation, VariableSupport { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -96,6 +101,12 @@ public class TextLookupInt implements Operation, VariableSupport { buffer.writeInt(indexId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); int dataSetId = buffer.readInt(); @@ -103,6 +114,11 @@ public class TextLookupInt implements Operation, VariableSupport { operations.add(new TextLookupInt(textId, dataSetId, indexId)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("Look up an array and turn into a text object") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java index 6d48f67ed8c8..d51b38924126 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java @@ -70,6 +70,11 @@ public class TextMeasure extends PaintOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -89,6 +94,12 @@ public class TextMeasure extends PaintOperation { buffer.writeInt(type); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); int textId = buffer.readInt(); @@ -96,9 +107,14 @@ public class TextMeasure extends PaintOperation { operations.add(new TextMeasure(id, textId, type)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) - .description("A float and its associated id") + .description("Measure text") .field(INT, "id", "id of float result of the measure") .field(INT, "textId", "id of text") .field(INT, "type", "type: measure 0=width,1=height"); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java index ecd5baaf0dd2..5b0c38fe996b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java @@ -29,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Operation to deal with Text data */ -public class TextMerge implements Operation { +public class TextMerge extends Operation { private static final int OP_CODE = Operations.TEXT_MERGE; private static final String CLASS_NAME = "TextMerge"; public int mTextId; @@ -58,6 +58,11 @@ public class TextMerge implements Operation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -77,6 +82,12 @@ public class TextMerge implements Operation { buffer.writeInt(srcId2); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); int srcId1 = buffer.readInt(); @@ -85,6 +96,11 @@ public class TextMerge implements Operation { operations.add(new TextMerge(textId, srcId1, srcId2)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Merge two string into one") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java index d265070a50bf..e329c38daf20 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java @@ -33,7 +33,7 @@ import java.util.List; * "tag" the subsequent operations to a given theme. On playback, we can then filter operations * depending on the chosen theme. */ -public class Theme implements RemoteComposeOperation { +public class Theme extends Operation implements RemoteComposeOperation { private static final int OP_CODE = Operations.THEME; private static final String CLASS_NAME = "Theme"; int mTheme; @@ -77,6 +77,11 @@ public class Theme implements RemoteComposeOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -86,11 +91,22 @@ public class Theme implements RemoteComposeOperation { buffer.writeInt(theme); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int theme = buffer.readInt(); operations.add(new Theme(theme)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Protocol Operations", OP_CODE, CLASS_NAME) .description("Set a theme") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java index 1bb7b2a183a2..e2e20bc5e57f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java @@ -42,12 +42,12 @@ import java.util.List; * touch behaviours. Including animating to Notched, positions. and tweaking the dynamics of the * animation. */ -public class TouchExpression implements Operation, VariableSupport, TouchListener { +public class TouchExpression extends Operation implements VariableSupport, TouchListener { private static final int OP_CODE = Operations.TOUCH_EXPRESSION; private static final String CLASS_NAME = "TouchExpression"; private float mDefValue; private float mOutDefValue; - public int mId; + private int mId; public float[] mSrcExp; int mMode = 1; // 0 = delta, 1 = absolute float mMax = 1; @@ -56,11 +56,14 @@ public class TouchExpression implements Operation, VariableSupport, TouchListene float mOutMin = 1; float mValue = 0; boolean mUnmodified = true; - public float[] mPreCalcValue; + private float[] mPreCalcValue; private float mLastChange = Float.NaN; private float mLastCalculatedValue = Float.NaN; AnimatedFloatExpression mExp = new AnimatedFloatExpression(); + + /** The maximum number of floats in the expression */ public static final int MAX_EXPRESSION_SIZE = 32; + private VelocityEasing mEasyTouch = new VelocityEasing(); private boolean mEasingToStop = false; private float mTouchUpTime = 0; @@ -495,6 +498,11 @@ public class TouchExpression implements Operation, VariableSupport, TouchListene return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -505,6 +513,14 @@ public class TouchExpression implements Operation, VariableSupport, TouchListene * @param buffer The buffer to write to * @param id the id of the resulting float * @param value the float expression array + * @param min the minimum allowed value + * @param max the maximum allowed value + * @param velocityId the velocity id + * @param touchEffects the type touch effect + * @param exp the expression the maps touch drags to movement + * @param touchMode the touch mode e.g. notch modes + * @param touchSpec the spec of the touch modes + * @param easingSpec the spec of when the object comes to an easing */ public static void apply( WireBuffer buffer, @@ -549,7 +565,13 @@ public class TouchExpression implements Operation, VariableSupport, TouchListene } } - public static void read(WireBuffer buffer, List<Operation> operations) { + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ + public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); float startValue = buffer.readFloat(); float min = buffer.readFloat(); @@ -594,6 +616,11 @@ public class TouchExpression implements Operation, VariableSupport, TouchListene easingData)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) .description("A Float expression") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java index baca3e0dc99a..de43b90840cc 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java @@ -19,34 +19,60 @@ import android.annotation.NonNull; /** Utilities to be used across all core operations */ public class Utils { + /** + * Convert an integer id into a float + * + * @param v the integer id to convert + * @return the id as an float + */ public static float asNan(int v) { return Float.intBitsToFloat(v | -0x800000); } + /** + * convert a float into an integer id + * + * @param value the float id to convert + * @return the id as an integer + */ public static int idFromNan(float value) { int b = Float.floatToRawIntBits(value); return b & 0x3FFFFF; } + /** + * convert a long into an ID + * + * @param v the long to convert + * @return the id still as a long + */ public static long idFromLong(long v) { return v - 0x100000000L; } + /** + * convert a float id and turn it into a string + * + * @param value float to convert + * @return string form of an id + */ @NonNull public static String idStringFromNan(float value) { int b = Float.floatToRawIntBits(value) & 0x3FFFFF; return idString(b); } + /** + * print an id as a string + * + * @param b the id + * @return the id as a string + */ @NonNull public static String idString(int b) { return (b > 0xFFFFF) ? "A_" + (b & 0xFFFFF) : "" + b; } - public static float getActualValue(float lr) { - return 0; - } - /** * trim a string to n characters if needing to trim end in "..." * diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java index 7f1d101cff16..0f840597e3c6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java @@ -18,12 +18,11 @@ package com.android.internal.widget.remotecompose.core.operations.layout; import android.annotation.NonNull; import com.android.internal.widget.remotecompose.core.CoreDocument; -import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; /** Operations representing actions on the document */ -public interface ActionOperation extends Operation { +public interface ActionOperation { void serializeToString(int indent, @NonNull StringSerializer serializer); void runAction( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java index aa8f75807141..121b18014a21 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java @@ -46,6 +46,11 @@ public class CanvasContent extends Component implements ComponentStartOperation return "CanvasContent"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_CANVAS_CONTENT; } @@ -61,6 +66,12 @@ public class CanvasContent extends Component implements ComponentStartOperation buffer.writeInt(componentId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); operations.add(new CanvasContent(componentId, 0, 0, 0, 0, null, -1)); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java index f44e20ddcdac..34c42494d964 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java @@ -182,6 +182,12 @@ public class ClickModifierOperation extends PaintOperation buffer.start(OP_CODE); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { operations.add(new ClickModifierOperation()); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java index fbfc796e092a..faa259f6b835 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java @@ -26,7 +26,6 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.SerializableToString; import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; -import com.android.internal.widget.remotecompose.core.operations.BitmapData; import com.android.internal.widget.remotecompose.core.operations.ComponentValue; import com.android.internal.widget.remotecompose.core.operations.TextData; import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimateMeasure; @@ -468,11 +467,6 @@ public class Component extends PaintOperation implements Measurable, Serializabl value[0] += mX; value[1] += mY; if (mParent != null) { - if (mParent instanceof LayoutComponent) { - LayoutComponent parent = (LayoutComponent) mParent; - value[0] += parent.getMarginLeft() + parent.getPaddingLeft(); - value[1] += parent.getMarginTop() + parent.getPaddingTop(); - } mParent.getLocationInWindow(value); } } @@ -658,12 +652,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl debugBox(this, context); } for (Operation op : mList) { - if (op instanceof BitmapData) { - ((BitmapData) op).apply(context.getContext()); - } - if (op instanceof PaintOperation) { - ((PaintOperation) op).paint(context); - } + op.apply(context.getContext()); } context.restore(); context.getContext().mLastComponent = prev; @@ -701,7 +690,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl if (applyAnimationAsNeeded(context)) { return; } - if (mVisibility == Visibility.GONE) { + if (mVisibility == Visibility.GONE || mVisibility == Visibility.INVISIBLE) { return; } paintingComponent(context); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java index 476b73c8fe7d..396644c45fa4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java @@ -25,7 +25,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; -public class ComponentEnd implements Operation { +public class ComponentEnd extends Operation { @Override public void write(@NonNull WireBuffer buffer) { @@ -54,6 +54,11 @@ public class ComponentEnd implements Operation { return "ComponentEnd"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.COMPONENT_END; } @@ -66,6 +71,12 @@ public class ComponentEnd implements Operation { return 1 + 4 + 4 + 4; } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { operations.add(new ComponentEnd()); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java index def9f780ba7f..a85ae270ffb1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java @@ -28,7 +28,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; -public class ComponentStart implements ComponentStartOperation { +public class ComponentStart extends Operation implements ComponentStartOperation { int mType = DEFAULT; float mX; @@ -162,6 +162,11 @@ public class ComponentStart implements ComponentStartOperation { return "ComponentStart"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.COMPONENT_START; } @@ -179,6 +184,12 @@ public class ComponentStart implements ComponentStartOperation { return 1 + 4 + 4 + 4; } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int type = buffer.readInt(); int componentId = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java index abf2356a3e49..a257d466839c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java @@ -15,6 +15,4 @@ */ package com.android.internal.widget.remotecompose.core.operations.layout; -import com.android.internal.widget.remotecompose.core.Operation; - -public interface ComponentStartOperation extends Operation {} +public interface ComponentStartOperation {} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java index 0041582f7189..7b0e4a2e2627 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java @@ -19,8 +19,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.OperationInterface; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.operations.BitmapData; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixSave; import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate; @@ -47,12 +49,6 @@ public class LayoutComponent extends Component { @Nullable protected ZIndexModifierOperation mZIndexModifier = null; @Nullable protected GraphicsLayerModifierOperation mGraphicsLayerModifier = null; - // Margins - protected float mMarginLeft = 0f; - protected float mMarginRight = 0f; - protected float mMarginTop = 0f; - protected float mMarginBottom = 0f; - protected float mPaddingLeft = 0f; protected float mPaddingRight = 0f; protected float mPaddingTop = 0f; @@ -76,22 +72,6 @@ public class LayoutComponent extends Component { super(parent, componentId, animationId, x, y, width, height); } - public float getMarginLeft() { - return mMarginLeft; - } - - public float getMarginRight() { - return mMarginRight; - } - - public float getMarginTop() { - return mMarginTop; - } - - public float getMarginBottom() { - return mMarginBottom; - } - public float getPaddingLeft() { return mPaddingLeft; } @@ -133,8 +113,7 @@ public class LayoutComponent extends Component { public void inflate() { ArrayList<TextData> data = new ArrayList<>(); - ArrayList<TouchExpression> touchExpressions = new ArrayList<>(); - ArrayList<PaintData> paintData = new ArrayList<>(); + ArrayList<Operation> supportedOperations = new ArrayList<>(); for (Operation op : mList) { if (op instanceof LayoutComponentContent) { @@ -179,10 +158,10 @@ public class LayoutComponent extends Component { mComponentModifiers.add((ModifierOperation) op); } else if (op instanceof TextData) { data.add((TextData) op); - } else if (op instanceof TouchExpression) { - touchExpressions.add((TouchExpression) op); - } else if (op instanceof PaintData) { - paintData.add((PaintData) op); + } else if (op instanceof TouchExpression + || (op instanceof PaintData) + || (op instanceof FloatExpression)) { + supportedOperations.add(op); } else { // nothing } @@ -190,8 +169,7 @@ public class LayoutComponent extends Component { mList.clear(); mList.addAll(data); - mList.addAll(touchExpressions); - mList.addAll(paintData); + mList.addAll(supportedOperations); mList.add(mComponentModifiers); for (Component c : mChildrenComponents) { c.mParent = this; @@ -203,10 +181,6 @@ public class LayoutComponent extends Component { mX = 0f; mY = 0f; - mMarginLeft = 0f; - mMarginTop = 0f; - mMarginRight = 0f; - mMarginBottom = 0f; mPaddingLeft = 0f; mPaddingTop = 0f; mPaddingRight = 0f; @@ -214,7 +188,7 @@ public class LayoutComponent extends Component { boolean applyHorizontalMargin = true; boolean applyVerticalMargin = true; - for (Operation op : mComponentModifiers.getList()) { + for (OperationInterface op : mComponentModifiers.getList()) { if (op instanceof PaddingModifierOperation) { // We are accumulating padding modifiers to compute the margin // until we hit a dimension; the computed padding for the @@ -223,31 +197,17 @@ public class LayoutComponent extends Component { float right = ((PaddingModifierOperation) op).getRight(); float top = ((PaddingModifierOperation) op).getTop(); float bottom = ((PaddingModifierOperation) op).getBottom(); - if (applyHorizontalMargin) { - mMarginLeft += left; - mMarginRight += right; - } - if (applyVerticalMargin) { - mMarginTop += top; - mMarginBottom += bottom; - } mPaddingLeft += left; mPaddingTop += top; mPaddingRight += right; mPaddingBottom += bottom; - } - if (op instanceof WidthModifierOperation && mWidthModifier == null) { + } else if (op instanceof WidthModifierOperation && mWidthModifier == null) { mWidthModifier = (WidthModifierOperation) op; - applyHorizontalMargin = false; - } - if (op instanceof HeightModifierOperation && mHeightModifier == null) { + } else if (op instanceof HeightModifierOperation && mHeightModifier == null) { mHeightModifier = (HeightModifierOperation) op; - applyVerticalMargin = false; - } - if (op instanceof ZIndexModifierOperation) { + } else if (op instanceof ZIndexModifierOperation) { mZIndexModifier = (ZIndexModifierOperation) op; - } - if (op instanceof GraphicsLayerModifierOperation) { + } else if (op instanceof GraphicsLayerModifierOperation) { mGraphicsLayerModifier = (GraphicsLayerModifierOperation) op; } } @@ -339,7 +299,7 @@ public class LayoutComponent extends Component { float s = 0f; float e = 0f; float w = 0f; - for (Operation c : mComponentModifiers.getList()) { + for (OperationInterface c : mComponentModifiers.getList()) { if (c instanceof WidthModifierOperation) { WidthModifierOperation o = (WidthModifierOperation) c; if (o.getType() == DimensionModifierOperation.Type.EXACT @@ -366,7 +326,7 @@ public class LayoutComponent extends Component { public float computeModifierDefinedPaddingWidth(@NonNull float[] padding) { float s = 0f; float e = 0f; - for (Operation c : mComponentModifiers.getList()) { + for (OperationInterface c : mComponentModifiers.getList()) { if (c instanceof PaddingModifierOperation) { PaddingModifierOperation pop = (PaddingModifierOperation) c; s += pop.getLeft(); @@ -383,7 +343,7 @@ public class LayoutComponent extends Component { float t = 0f; float b = 0f; float h = 0f; - for (Operation c : mComponentModifiers.getList()) { + for (OperationInterface c : mComponentModifiers.getList()) { if (c instanceof HeightModifierOperation) { HeightModifierOperation o = (HeightModifierOperation) c; if (o.getType() == DimensionModifierOperation.Type.EXACT @@ -410,7 +370,7 @@ public class LayoutComponent extends Component { public float computeModifierDefinedPaddingHeight(@NonNull float[] padding) { float t = 0f; float b = 0f; - for (Operation c : mComponentModifiers.getList()) { + for (OperationInterface c : mComponentModifiers.getList()) { if (c instanceof PaddingModifierOperation) { PaddingModifierOperation pop = (PaddingModifierOperation) c; t += pop.getTop(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java index 7eea885f6b6d..20e4688aaa32 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java @@ -46,6 +46,11 @@ public class LayoutComponentContent extends Component implements ComponentStartO return "LayoutContent"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_CONTENT; } @@ -61,6 +66,12 @@ public class LayoutComponentContent extends Component implements ComponentStartO buffer.writeInt(componentId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); operations.add(new LayoutComponentContent(componentId, 0, 0, 0, 0, null, -1)); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java index 71de2857258b..d88f711c62c6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java @@ -25,7 +25,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; -public class LoopEnd implements Operation { +public class LoopEnd extends Operation { @Override public void write(@NonNull WireBuffer buffer) { @@ -54,6 +54,11 @@ public class LoopEnd implements Operation { return "LoopEnd"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LOOP_END; } @@ -62,6 +67,12 @@ public class LoopEnd implements Operation { buffer.start(id()); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { operations.add(new LoopEnd()); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java index d88382dde7e0..83a2f0e1ffa3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java @@ -21,31 +21,57 @@ import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.operations.Utils; import java.util.ArrayList; import java.util.List; /** Represents a loop of operations */ -public class LoopOperation extends PaintOperation { +public class LoopOperation extends PaintOperation implements VariableSupport { private static final int OP_CODE = Operations.LOOP_START; @NonNull public ArrayList<Operation> mList = new ArrayList<>(); int mIndexVariableId; - float mUntil = 12; - float mFrom = 0; - float mStep = 1; + float mUntil; + float mFrom; + float mStep; + float mUntilOut; + float mFromOut; + float mStepOut; public LoopOperation(int count, int indexId) { mUntil = count; mIndexVariableId = indexId; } - public LoopOperation(float count, float from, float step, int indexId) { - mUntil = count; + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mUntil)) { + context.listensTo(Utils.idFromNan(mUntil), this); + } + if (Float.isNaN(mFrom)) { + context.listensTo(Utils.idFromNan(mFrom), this); + } + if (Float.isNaN(mStep)) { + context.listensTo(Utils.idFromNan(mStep), this); + } + } + + @Override + public void updateVariables(RemoteContext context) { + mUntilOut = Float.isNaN(mUntil) ? context.getFloat(Utils.idFromNan(mUntil)) : mUntil; + mFromOut = Float.isNaN(mFrom) ? context.getFloat(Utils.idFromNan(mFrom)) : mFrom; + mStepOut = Float.isNaN(mStep) ? context.getFloat(Utils.idFromNan(mStep)) : mStep; + } + + public LoopOperation(int indexId, float from, float step, float until) { + mUntil = until; mFrom = from; mStep = step; mIndexVariableId = indexId; @@ -58,13 +84,19 @@ public class LoopOperation extends PaintOperation { @Override public void write(@NonNull WireBuffer buffer) { - apply(buffer, mUntil, mFrom, mStep, mIndexVariableId); + apply(buffer, mIndexVariableId, mFrom, mStep, mUntil); } @NonNull @Override public String toString() { - return "LoopOperation"; + StringBuilder builder = new StringBuilder("LoopOperation\n"); + for (Operation operation : mList) { + builder.append(" "); + builder.append(operation); + builder.append("\n"); + } + return builder.toString(); } @NonNull @@ -76,13 +108,13 @@ public class LoopOperation extends PaintOperation { @Override public void paint(@NonNull PaintContext context) { if (mIndexVariableId == 0) { - for (float i = mFrom; i < mUntil; i += mStep) { + for (float i = mFromOut; i < mUntilOut; i += mStepOut) { for (Operation op : mList) { op.apply(context.getContext()); } } } else { - for (float i = mFrom; i < mUntil; i += mStep) { + for (float i = mFromOut; i < mUntilOut; i += mStepOut) { context.getContext().loadFloat(mIndexVariableId, i); for (Operation op : mList) { if (op instanceof VariableSupport) { @@ -100,24 +132,34 @@ public class LoopOperation extends PaintOperation { } public static void apply( - @NonNull WireBuffer buffer, float count, float from, float step, int indexId) { + @NonNull WireBuffer buffer, int indexId, float from, float step, float until) { buffer.start(OP_CODE); - buffer.writeFloat(count); + buffer.writeInt(indexId); buffer.writeFloat(from); buffer.writeFloat(step); - buffer.writeInt(indexId); + buffer.writeFloat(until); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { - float count = buffer.readFloat(); + int indexId = buffer.readInt(); float from = buffer.readFloat(); float step = buffer.readFloat(); - int indexId = buffer.readInt(); - operations.add(new LoopOperation(count, from, step, indexId)); + float until = buffer.readFloat(); + operations.add(new LoopOperation(indexId, from, step, until)); } public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Operations", OP_CODE, name()) - .description("Loop. This operation execute" + " a list of action in a loop"); + .description("Loop. This operation execute" + " a list of action in a loop") + .field(DocumentedOperation.INT, "id", "if not 0 write value") + .field(DocumentedOperation.FLOAT, "from", "values starts at") + .field(DocumentedOperation.FLOAT, "step", "value step") + .field(DocumentedOperation.FLOAT, "until", "stops less than or equal"); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java index ca79003448e7..99b7e68786fb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java @@ -25,7 +25,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; -public class OperationsListEnd implements Operation { +public class OperationsListEnd extends Operation { @Override public void write(@NonNull WireBuffer buffer) { @@ -54,6 +54,11 @@ public class OperationsListEnd implements Operation { return "ListEnd"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.OPERATIONS_LIST_END; } @@ -62,6 +67,12 @@ public class OperationsListEnd implements Operation { buffer.start(id()); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { operations.add(new OperationsListEnd()); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java index 85c71537540c..fd1628729dd4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java @@ -197,6 +197,11 @@ public class RootLayoutComponent extends Component implements ComponentStartOper return "RootLayout"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_ROOT; } @@ -206,6 +211,12 @@ public class RootLayoutComponent extends Component implements ComponentStartOper buffer.writeInt(componentId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1)); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java index 0316f96bfc3e..3185bb5f0f72 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java @@ -84,6 +84,12 @@ public class TouchCancelModifierOperation extends ListActionsOperation implement buffer.start(OP_CODE); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(WireBuffer buffer, List<Operation> operations) { operations.add(new TouchCancelModifierOperation()); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java index 6fb705985711..b230b09112b2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java @@ -29,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.easin import java.util.List; /** Basic component animation spec */ -public class AnimationSpec implements Operation { +public class AnimationSpec extends Operation { int mAnimationId = -1; int mMotionDuration = 300; int mMotionEasingType = GeneralEasing.CUBIC_STANDARD; @@ -142,6 +142,11 @@ public class AnimationSpec implements Operation { return "AnimationSpec"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.ANIMATION_SPEC; } @@ -193,6 +198,12 @@ public class AnimationSpec implements Operation { buffer.writeInt(animationToInt(exitAnimation)); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int animationId = buffer.readInt(); int motionDuration = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java index 47a942187900..01cd7ccd0b94 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java @@ -108,6 +108,8 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation @NonNull PaintContext context, float maxWidth, float maxHeight, + boolean horizontalWrap, + boolean verticalWrap, @NonNull MeasurePass measure, @NonNull Size size) { for (Component c : mChildrenComponents) { @@ -175,6 +177,11 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation return "BoxLayout"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_BOX; } @@ -192,6 +199,12 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation buffer.writeInt(verticalPositioning); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); int animationId = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java index 476b1a666fe9..665db2637674 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java @@ -77,6 +77,11 @@ public class CanvasLayout extends BoxLayout { return "CanvasLayout"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_CANVAS; } @@ -87,6 +92,12 @@ public class CanvasLayout extends BoxLayout { buffer.writeInt(animationId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); int animationId = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java index 68e18c699539..5b9ee0ff511f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java @@ -125,17 +125,21 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati @NonNull PaintContext context, float maxWidth, float maxHeight, + boolean horizontalWrap, + boolean verticalWrap, @NonNull MeasurePass measure, @NonNull Size size) { DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")"); int visibleChildrens = 0; + float currentMaxHeight = maxHeight; for (Component c : mChildrenComponents) { - c.measure(context, 0f, maxWidth, 0f, maxHeight, measure); + c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure); ComponentMeasure m = measure.get(c); if (m.getVisibility() != Visibility.GONE) { size.setWidth(Math.max(size.getWidth(), m.getW())); size.setHeight(size.getHeight() + m.getH()); visibleChildrens++; + currentMaxHeight -= m.getH(); } } if (!mChildrenComponents.isEmpty()) { @@ -342,6 +346,11 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati return "ColumnLayout"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_COLUMN; } @@ -361,6 +370,12 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati buffer.writeFloat(spacedBy); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); int animationId = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java index 3b5aaf38fb5b..6a15b7f1b178 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java @@ -53,6 +53,8 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl @NonNull PaintContext context, float maxWidth, float maxHeight, + boolean horizontalWrap, + boolean verticalWrap, @NonNull MeasurePass measure, @NonNull Size size) { // nothing here @@ -129,42 +131,67 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl float maxHeight, @NonNull MeasurePass measure) { boolean hasWrap = true; - float measuredWidth = - Math.min(maxWidth, computeModifierDefinedWidth() - mMarginLeft - mMarginRight); - float measuredHeight = - Math.min(maxHeight, computeModifierDefinedHeight() - mMarginTop - mMarginBottom); - float insetMaxWidth = maxWidth - mMarginLeft - mMarginRight; - float insetMaxHeight = maxHeight - mMarginTop - mMarginBottom; + + float measuredWidth = Math.min(maxWidth, computeModifierDefinedWidth()); + float measuredHeight = Math.min(maxHeight, computeModifierDefinedHeight()); + float insetMaxWidth = maxWidth - mPaddingLeft - mPaddingRight; + float insetMaxHeight = maxHeight - mPaddingTop - mPaddingBottom; + if (mWidthModifier.isIntrinsicMin()) { maxWidth = intrinsicWidth(); } if (mHeightModifier.isIntrinsicMin()) { maxHeight = intrinsicHeight(); } - if (mWidthModifier.isWrap() || mHeightModifier.isWrap()) { // TODO: potential npe -- bbade@ + + boolean hasHorizontalWrap = mWidthModifier.isWrap(); + boolean hasVerticalWrap = mHeightModifier.isWrap(); + if (hasHorizontalWrap || hasVerticalWrap) { // TODO: potential npe -- bbade@ mCachedWrapSize.setWidth(0f); mCachedWrapSize.setHeight(0f); - computeWrapSize(context, maxWidth, maxHeight, measure, mCachedWrapSize); + float wrapMaxWidth = insetMaxWidth; + float wrapMaxHeight = insetMaxHeight; + if (hasHorizontalWrap) { + wrapMaxWidth = insetMaxWidth - mPaddingLeft - mPaddingRight; + } + if (hasVerticalWrap) { + wrapMaxHeight = insetMaxHeight - mPaddingTop - mPaddingBottom; + } + computeWrapSize( + context, + wrapMaxWidth, + wrapMaxHeight, + mWidthModifier.isWrap(), + mHeightModifier.isWrap(), + measure, + mCachedWrapSize); measuredWidth = mCachedWrapSize.getWidth(); + if (hasHorizontalWrap) { + measuredWidth += mPaddingLeft + mPaddingRight; + } measuredHeight = mCachedWrapSize.getHeight(); + if (hasVerticalWrap) { + measuredHeight += mPaddingTop + mPaddingBottom; + } } else { hasWrap = false; } + if (isInHorizontalFill()) { - measuredWidth = insetMaxWidth; + measuredWidth = maxWidth; } else if (mWidthModifier.hasWeight()) { measuredWidth = Math.max(measuredWidth, computeModifierDefinedWidth()); } else { measuredWidth = Math.max(measuredWidth, minWidth); - measuredWidth = Math.min(measuredWidth, insetMaxWidth); + measuredWidth = Math.min(measuredWidth, maxWidth); } if (isInVerticalFill()) { // todo: potential npe -- bbade@ - measuredHeight = insetMaxHeight; + measuredHeight = maxHeight; } else if (mHeightModifier.hasWeight()) { measuredHeight = Math.max(measuredHeight, computeModifierDefinedHeight()); } else { measuredHeight = Math.max(measuredHeight, minHeight); - measuredHeight = Math.min(measuredHeight, insetMaxHeight); + measuredHeight = Math.min(measuredHeight, maxHeight); } if (minWidth == maxWidth) { measuredWidth = maxWidth; @@ -172,20 +199,27 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl if (minHeight == maxHeight) { measuredHeight = maxHeight; } - measuredWidth = Math.min(measuredWidth, insetMaxWidth); - measuredHeight = Math.min(measuredHeight, insetMaxHeight); + if (!hasWrap) { if (hasHorizontalScroll()) { mCachedWrapSize.setWidth(0f); mCachedWrapSize.setHeight(0f); - computeWrapSize(context, Float.MAX_VALUE, maxHeight, measure, mCachedWrapSize); + computeWrapSize( + context, + Float.MAX_VALUE, + maxHeight, + false, + false, + measure, + mCachedWrapSize); float w = mCachedWrapSize.getWidth(); computeSize(context, 0f, w, 0, measuredHeight, measure); mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w); } else if (hasVerticalScroll()) { mCachedWrapSize.setWidth(0f); mCachedWrapSize.setHeight(0f); - computeWrapSize(context, maxWidth, Float.MAX_VALUE, measure, mCachedWrapSize); + computeWrapSize( + context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize); float h = mCachedWrapSize.getHeight(); computeSize(context, 0f, measuredWidth, 0, h, measure); mComponentModifiers.setVerticalScrollDimension(measuredHeight, h); @@ -202,9 +236,6 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl cm.setH(measuredHeight); } - measuredWidth += mMarginLeft + mMarginRight; - measuredHeight += mMarginTop + mMarginBottom; - ComponentMeasure m = measure.get(this); m.setW(measuredWidth); m.setH(measuredHeight); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java index 0ce634f9c1ab..0ec820b85964 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java @@ -123,21 +123,25 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation @NonNull PaintContext context, float maxWidth, float maxHeight, + boolean horizontalWrap, + boolean verticalWrap, @NonNull MeasurePass measure, @NonNull Size size) { DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")"); - // int visibleChildrens = 0; + int visibleChildrens = 0; + float currentMaxWidth = maxWidth; for (Component c : mChildrenComponents) { - c.measure(context, 0f, maxWidth, 0f, maxHeight, measure); + c.measure(context, 0f, currentMaxWidth, 0f, maxHeight, measure); ComponentMeasure m = measure.get(c); if (m.getVisibility() != Visibility.GONE) { size.setWidth(size.getWidth() + m.getW()); size.setHeight(Math.max(size.getHeight(), m.getH())); - // visibleChildrens++; + visibleChildrens++; + currentMaxWidth -= m.getW(); } } if (!mChildrenComponents.isEmpty()) { - size.setWidth(size.getWidth() + (mSpacedBy * (mChildrenComponents.size() - 1))); + size.setWidth(size.getWidth() + (mSpacedBy * (visibleChildrens - 1))); } DebugLog.e(); } @@ -345,6 +349,11 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation return "RowLayout"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_ROW; } @@ -364,6 +373,12 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation buffer.writeFloat(spacedBy); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); int animationId = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java index 73a104bcafa6..61a3ec964142 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java @@ -159,10 +159,13 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio @NonNull PaintContext context, float maxWidth, float maxHeight, + boolean horizontalWrap, + boolean verticalWrap, @NonNull MeasurePass measure, @NonNull Size size) { LayoutManager layout = getLayout(currentLayoutIndex); - layout.computeWrapSize(context, maxWidth, maxHeight, measure, size); + layout.computeWrapSize( + context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size); } @Override @@ -442,11 +445,7 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio int id = c.getPaintId(); for (int i = 0; i < idIndex; i++) { if (cacheListElementsId[i] == id) { - context.translate( - previousLayout.getMarginLeft(), previousLayout.getMarginTop()); c.paint(context); - context.translate( - -currentLayout.getMarginLeft(), -currentLayout.getMarginTop()); break; } } @@ -472,16 +471,10 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio // and fade in the new one Component previousComponent = stateComponents[previousLayoutIndex]; if (previousComponent != null && component != previousComponent) { - context.translate( - currentLayout.getMarginLeft(), currentLayout.getMarginTop()); previousComponent.paint(context); - context.translate( - -currentLayout.getMarginLeft(), -currentLayout.getMarginTop()); } } - context.translate(currentLayout.getMarginLeft(), currentLayout.getMarginTop()); component.paint(context); - context.translate(-currentLayout.getMarginLeft(), -currentLayout.getMarginTop()); } else if (op instanceof PaintOperation) { ((PaintOperation) op).paint(context); } @@ -563,6 +556,12 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio buffer.writeInt(indexId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); int animationId = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java index a527e5ad4077..8e7f538d0004 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java @@ -52,8 +52,8 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation private int mType = -1; private float mTextX; private float mTextY; - private float mTextW; - private float mTextH; + private float mTextW = -1; + private float mTextH = -1; @Nullable private String mCachedString = ""; @@ -66,7 +66,11 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation @Override public void updateVariables(@NonNull RemoteContext context) { - mCachedString = context.getText(mTextId); + String cachedString = context.getText(mTextId); + if (cachedString != null && cachedString.equalsIgnoreCase(mCachedString)) { + return; + } + mCachedString = cachedString; if (mType == -1) { if (mFontFamilyId != -1) { String fontFamily = context.getText(mFontFamilyId); @@ -86,8 +90,9 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation mType = 0; } } - mNeedsMeasure = true; - needsRepaint(); + mTextW = -1; + mTextH = -1; + invalidateMeasure(); } public TextLayout( @@ -168,7 +173,14 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation return; } int length = mCachedString.length(); - context.drawTextRun(mTextId, 0, length, 0, 0, mTextX, mTextY, false); + if (mTextW > mWidth) { + context.save(); + context.translate(getScrollX(), getScrollY()); + context.drawTextRun(mTextId, 0, length, 0, 0, mTextX, mTextY, false); + context.restore(); + } else { + context.drawTextRun(mTextId, 0, length, 0, 0, mTextX, mTextY, false); + } if (DEBUG) { mPaint.setStyle(PaintBundle.STYLE_FILL_AND_STROKE); mPaint.setColor(1f, 1F, 1F, 1F); @@ -246,6 +258,8 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation @NonNull PaintContext context, float maxWidth, float maxHeight, + boolean horizontalWrap, + boolean verticalWrap, @NonNull MeasurePass measure, @NonNull Size size) { context.savePaint(); @@ -262,9 +276,9 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation context.restorePaint(); float w = bounds[2] - bounds[0]; float h = bounds[3] - bounds[1]; - size.setWidth(w); + size.setWidth(Math.min(maxWidth, w)); mTextX = -bounds[0]; - size.setHeight(h); + size.setHeight(Math.min(maxHeight, h)); mTextY = -bounds[1]; mTextW = w; mTextH = h; @@ -285,6 +299,11 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation return "TextLayout"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.LAYOUT_TEXT; } @@ -312,6 +331,12 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation buffer.writeInt(textAlign); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int componentId = buffer.readInt(); int animationId = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java index 71d2ba6edf21..5df16c5bc03a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java @@ -114,6 +114,11 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -142,6 +147,12 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation { buffer.writeInt(shapeType); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { float x = buffer.readFloat(); float y = buffer.readFloat(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java index 0707cd627678..bfadd2f1ef9c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java @@ -160,6 +160,11 @@ public class BorderModifierOperation extends DecoratorModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -192,6 +197,12 @@ public class BorderModifierOperation extends DecoratorModifierOperation { buffer.writeInt(shapeType); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { float x = buffer.readFloat(); float y = buffer.readFloat(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java index e05b02781e10..d0af872acc53 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java @@ -60,6 +60,11 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -68,6 +73,12 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation { buffer.start(OP_CODE); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { operations.add(new ClipRectModifierOperation()); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java index 471db0bedb11..1e6ccfcb5d34 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java @@ -34,7 +34,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Allows setting visibility on a component */ -public class ComponentVisibilityOperation +public class ComponentVisibilityOperation extends Operation implements ModifierOperation, VariableSupport, DecoratorComponent { private static final int OP_CODE = Operations.MODIFIER_VISIBILITY; @@ -79,6 +79,12 @@ public class ComponentVisibilityOperation buffer.writeInt(valueId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int valueId = buffer.readInt(); operations.add(new ComponentVisibilityOperation(valueId)); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java index b9324f0320d3..b11deae3d196 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java @@ -17,13 +17,15 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie import android.annotation.NonNull; +import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; /** Base class for dimension modifiers */ -public abstract class DimensionModifierOperation implements ModifierOperation, VariableSupport { +public abstract class DimensionModifierOperation extends Operation + implements ModifierOperation, VariableSupport { public enum Type { EXACT, diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java index 571e554b9c71..4252309b7e4c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java @@ -206,6 +206,11 @@ public class GraphicsLayerModifierOperation extends DecoratorModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java index 7bb4a756afa5..692b5269954a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java @@ -37,6 +37,11 @@ public class HeightModifierOperation extends DimensionModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -47,6 +52,12 @@ public class HeightModifierOperation extends DimensionModifierOperation { buffer.writeFloat(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Type type = Type.fromInt(buffer.readInt()); float value = buffer.readFloat(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java index d239bc857446..333e281d4abb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java @@ -32,7 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Capture a host action information. This can be triggered on eg. a click. */ -public class HostActionOperation implements ActionOperation { +public class HostActionOperation extends Operation implements ActionOperation { private static final int OP_CODE = Operations.HOST_ACTION; int mActionId = -1; @@ -88,6 +88,12 @@ public class HostActionOperation implements ActionOperation { buffer.writeInt(actionId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int actionId = buffer.readInt(); operations.add(new HostActionOperation(actionId)); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java index 3268e5efd449..f9a4270905a1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java @@ -32,7 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Capture a host action information. This can be triggered on eg. a click. */ -public class HostNamedActionOperation implements ActionOperation { +public class HostNamedActionOperation extends Operation implements ActionOperation { private static final int OP_CODE = Operations.HOST_NAMED_ACTION; public static final int FLOAT_TYPE = 0; @@ -112,6 +112,12 @@ public class HostNamedActionOperation implements ActionOperation { buffer.writeInt(valueId); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int textId = buffer.readInt(); int type = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java index 8f08f1417add..f8926fef56fa 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java @@ -17,10 +17,10 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie import android.annotation.NonNull; -import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.OperationInterface; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; /** Represents a modifier */ -public interface ModifierOperation extends Operation { +public interface ModifierOperation extends OperationInterface { void serializeToString(int indent, @NonNull StringSerializer serializer); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java index 8c07059369ab..69c4e9a8e423 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java @@ -94,6 +94,11 @@ public class OffsetModifierOperation extends DecoratorModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java index 2b6621e4fd58..545df64ab154 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java @@ -32,7 +32,7 @@ import java.util.List; * Represents a padding modifier. Padding modifiers can be chained and will impact following * modifiers. */ -public class PaddingModifierOperation implements ModifierOperation { +public class PaddingModifierOperation extends Operation implements ModifierOperation { private static final int OP_CODE = Operations.MODIFIER_PADDING; public static final String CLASS_NAME = "PaddingModifierOperation"; float mLeft; @@ -118,6 +118,11 @@ public class PaddingModifierOperation implements ModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.MODIFIER_PADDING; } @@ -131,6 +136,12 @@ public class PaddingModifierOperation implements ModifierOperation { buffer.writeFloat(bottom); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { float left = buffer.readFloat(); float top = buffer.readFloat(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java index 3fefc5817270..681501d9cdf9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java @@ -37,11 +37,22 @@ public class RoundedClipRectModifierOperation extends DrawBase4 public static final int OP_CODE = Operations.MODIFIER_ROUNDED_CLIP_RECT; public static final String CLASS_NAME = "RoundedClipRectModifierOperation"; + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Maker m = RoundedClipRectModifierOperation::new; read(m, buffer, operations); } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java index 8dcfed999c5c..0b6632057bd2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java @@ -134,6 +134,11 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java index a97fcffdf75c..b96d3cc4bbc0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java @@ -33,7 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Apply a value change on an float variable. */ -public class ValueFloatChangeActionOperation implements ActionOperation { +public class ValueFloatChangeActionOperation extends Operation implements ActionOperation { private static final int OP_CODE = Operations.VALUE_FLOAT_CHANGE_ACTION; int mTargetValueId = -1; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java index 41586b4939a6..d81b7ffd1ef9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java @@ -32,7 +32,8 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Apply a value change on an integer variable. */ -public class ValueFloatExpressionChangeActionOperation implements ActionOperation { +public class ValueFloatExpressionChangeActionOperation extends Operation + implements ActionOperation { private static final int OP_CODE = Operations.VALUE_FLOAT_EXPRESSION_CHANGE_ACTION; int mTargetValueId = -1; @@ -88,6 +89,12 @@ public class ValueFloatExpressionChangeActionOperation implements ActionOperatio buffer.writeInt(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int valueId = buffer.readInt(); int value = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java index c2cd2ab32bb7..fb13b42dbd21 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java @@ -32,7 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Apply a value change on an integer variable. */ -public class ValueIntegerChangeActionOperation implements ActionOperation { +public class ValueIntegerChangeActionOperation extends Operation implements ActionOperation { private static final int OP_CODE = Operations.VALUE_INTEGER_CHANGE_ACTION; int mTargetValueId = -1; @@ -87,6 +87,12 @@ public class ValueIntegerChangeActionOperation implements ActionOperation { buffer.writeInt(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int valueId = buffer.readInt(); int value = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java index 43fbb8546b9d..0fe88ad165a9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java @@ -32,7 +32,8 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Apply a value change on an integer variable. */ -public class ValueIntegerExpressionChangeActionOperation implements ActionOperation { +public class ValueIntegerExpressionChangeActionOperation extends Operation + implements ActionOperation { private static final int OP_CODE = Operations.VALUE_INTEGER_EXPRESSION_CHANGE_ACTION; long mTargetValueId = -1; @@ -88,6 +89,12 @@ public class ValueIntegerExpressionChangeActionOperation implements ActionOperat buffer.writeLong(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { long valueId = buffer.readLong(); long value = buffer.readLong(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java index 1107889faaab..a8d3b87f04b4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java @@ -32,7 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; /** Apply a value change on a string variable. */ -public class ValueStringChangeActionOperation implements ActionOperation { +public class ValueStringChangeActionOperation extends Operation implements ActionOperation { private static final int OP_CODE = Operations.VALUE_STRING_CHANGE_ACTION; int mTargetValueId = -1; @@ -91,6 +91,12 @@ public class ValueStringChangeActionOperation implements ActionOperation { buffer.writeInt(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int valueId = buffer.readInt(); int value = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java index 3c757a893a57..f6d743f599d3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java @@ -37,6 +37,11 @@ public class WidthModifierOperation extends DimensionModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } @@ -47,6 +52,12 @@ public class WidthModifierOperation extends DimensionModifierOperation { buffer.writeFloat(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { Type type = Type.fromInt(buffer.readInt()); float value = buffer.readFloat(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java index 82c8f343565e..96ed2cda3e10 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java @@ -83,6 +83,11 @@ public class ZIndexModifierOperation extends DecoratorModifierOperation { return CLASS_NAME; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return OP_CODE; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java index 07cf7627e24d..95434696abdc 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java @@ -498,6 +498,11 @@ public class PaintBundle { return ret; } + /** + * Write a bundle of paint changes to the buffer + * + * @param buffer bundle to write + */ public void writeBundle(@NonNull WireBuffer buffer) { buffer.writeInt(mPos); for (int index = 0; index < mPos; index++) { @@ -505,6 +510,11 @@ public class PaintBundle { } } + /** + * This will read the paint bundle off the wire buffer + * + * @param buffer the buffer to read + */ public void readBundle(@NonNull WireBuffer buffer) { int len = buffer.readInt(); if (len <= 0 || len > 1024) { @@ -587,6 +597,9 @@ public class PaintBundle { public static final int RADIAL_GRADIENT = 1; public static final int SWEEP_GRADIENT = 2; + private int mLastShaderSet = -1; + private boolean mColorFilterSet = false; + /** * sets a shader that draws a linear gradient along a line. * @@ -722,12 +735,14 @@ public class PaintBundle { mArray[mPos] = COLOR_FILTER_ID | (mode << 16); mPos++; mArray[mPos++] = color; + mColorFilterSet = true; } /** This sets the color filter to null */ public void clearColorFilter() { mArray[mPos] = CLEAR_COLOR_FILTER; mPos++; + mColorFilterSet = false; } /** @@ -843,6 +858,7 @@ public class PaintBundle { * @param shaderId */ public void setShader(int shaderId) { + mLastShaderSet = shaderId; mArray[mPos] = SHADER; mPos++; mArray[mPos] = shaderId; @@ -909,11 +925,23 @@ public class PaintBundle { mPos++; } + /** + * clear a series of paint parameters. Currently not used + * + * @param mask bit pattern of the attributes to clear + */ public void clear(long mask) { // unused for now } + /** Reset the content of the paint bundle so that it can be reused */ public void reset() { mPos = 0; + if (mColorFilterSet) { + clearColorFilter(); + } + if (mLastShaderSet != -1 && mLastShaderSet != 0) { + setShader(0); + } } public static @NonNull String blendModeString(int mode) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java index e5633c70faee..a56874781e4a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java @@ -18,7 +18,6 @@ package com.android.internal.widget.remotecompose.core.operations.utilities; import android.annotation.NonNull; import android.annotation.Nullable; -import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.MonotonicSpline; /** high performance floating point expression evaluator used in animation */ @@ -141,7 +140,7 @@ public class AnimatedFloatExpression { for (int i = 0; i < mStack.length; i++) { float v = mStack[i]; if (Float.isNaN(v)) { - sp = mOps[fromNaN(v) - OFFSET].eval(sp); + sp = opEval(sp, fromNaN(v)); } else { mStack[++sp] = v; } @@ -170,7 +169,7 @@ public class AnimatedFloatExpression { if (Float.isNaN(v)) { int id = fromNaN(v); if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) { - sp = mOps[id - OFFSET].eval(sp); + sp = opEval(sp, id); } else { mStack[++sp] = v; } @@ -199,7 +198,7 @@ public class AnimatedFloatExpression { if (Float.isNaN(v)) { int id = fromNaN(v); if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) { - sp = mOps[id - OFFSET].eval(sp); + sp = opEval(sp, id); } else { mStack[++sp] = v; } @@ -228,10 +227,11 @@ public class AnimatedFloatExpression { mStack = mLocalStack; mVar = var; int sp = -1; + for (int i = 0; i < len; i++) { float v = mStack[i]; if (Float.isNaN(v)) { - sp = mOps[fromNaN(v) - OFFSET].eval(sp); + sp = opEval(sp, fromNaN(v)); } else { mStack[++sp] = v; } @@ -250,9 +250,10 @@ public class AnimatedFloatExpression { mStack = exp; mVar = var; int sp = -1; + for (float v : exp) { if (Float.isNaN(v)) { - sp = mOps[fromNaN(v) - OFFSET].eval(sp); + sp = opEval(sp, fromNaN(v)); } else { System.out.print(" " + v); mStack[++sp] = v; @@ -261,294 +262,6 @@ public class AnimatedFloatExpression { return mStack[sp]; } - @NonNull Op[] mOps; - - { - Op mADD = - (sp) -> { // ADD - mStack[sp - 1] = mStack[sp - 1] + mStack[sp]; - return sp - 1; - }; - Op mSUB = - (sp) -> { // SUB - mStack[sp - 1] = mStack[sp - 1] - mStack[sp]; - return sp - 1; - }; - Op mMUL = - (sp) -> { // MUL - mStack[sp - 1] = mStack[sp - 1] * mStack[sp]; - return sp - 1; - }; - Op mDIV = - (sp) -> { // DIV - mStack[sp - 1] = mStack[sp - 1] / mStack[sp]; - return sp - 1; - }; - Op mMOD = - (sp) -> { // MOD - mStack[sp - 1] = mStack[sp - 1] % mStack[sp]; - return sp - 1; - }; - Op mMIN = - (sp) -> { // MIN - mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]); - return sp - 1; - }; - Op mMAX = - (sp) -> { // MAX - mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]); - return sp - 1; - }; - Op mPOW = - (sp) -> { // POW - mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]); - return sp - 1; - }; - Op mSQRT = - (sp) -> { // SQRT - mStack[sp] = (float) Math.sqrt(mStack[sp]); - return sp; - }; - Op mABS = - (sp) -> { // ABS - mStack[sp] = (float) Math.abs(mStack[sp]); - return sp; - }; - Op mSIGN = - (sp) -> { // SIGN - mStack[sp] = (float) Math.signum(mStack[sp]); - return sp; - }; - Op mCOPY_SIGN = - (sp) -> { // copySign - mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]); - return sp - 1; - }; - Op mEXP = - (sp) -> { // EXP - mStack[sp] = (float) Math.exp(mStack[sp]); - return sp; - }; - Op mFLOOR = - (sp) -> { // FLOOR - mStack[sp] = (float) Math.floor(mStack[sp]); - return sp; - }; - Op mLOG = - (sp) -> { // LOG - mStack[sp] = (float) Math.log10(mStack[sp]); - return sp; - }; - Op mLN = - (sp) -> { // LN - mStack[sp] = (float) Math.log(mStack[sp]); - return sp; - }; - Op mROUND = - (sp) -> { // ROUND - mStack[sp] = (float) Math.round(mStack[sp]); - return sp; - }; - Op mSIN = - (sp) -> { // SIN - mStack[sp] = (float) Math.sin(mStack[sp]); - return sp; - }; - Op mCOS = - (sp) -> { // COS - mStack[sp] = (float) Math.cos(mStack[sp]); - return sp; - }; - Op mTAN = - (sp) -> { // TAN - mStack[sp] = (float) Math.tan(mStack[sp]); - return sp; - }; - Op mASIN = - (sp) -> { // ASIN - mStack[sp] = (float) Math.asin(mStack[sp]); - return sp; - }; - Op mACOS = - (sp) -> { // ACOS - mStack[sp] = (float) Math.acos(mStack[sp]); - return sp; - }; - Op mATAN = - (sp) -> { // ATAN - mStack[sp] = (float) Math.atan(mStack[sp]); - return sp; - }; - Op mATAN2 = - (sp) -> { // ATAN2 - mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]); - return sp - 1; - }; - Op mMAD = - (sp) -> { // MAD - mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2]; - return sp - 2; - }; - Op mTERNARY_CONDITIONAL = - (sp) -> { // TERNARY_CONDITIONAL - mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2]; - return sp - 2; - }; - Op mCLAMP = - (sp) -> { // CLAMP (min, max, value) - mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]); - return sp - 2; - }; - Op mCBRT = - (sp) -> { // CBRT is cube root - mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.); - return sp; - }; - Op mDEG = - (sp) -> { // DEG - mStack[sp] = mStack[sp] * FP_TO_RAD; - return sp; - }; - Op mRAD = - (sp) -> { // RAD - mStack[sp] = mStack[sp] * FP_TO_DEG; - return sp; - }; - Op mCEIL = - (sp) -> { // CEIL - mStack[sp] = (float) Math.ceil(mStack[sp]); - return sp; - }; - Op mA_DEREF = - (sp) -> { // A_DEREF - Utils.log(" \n >>> DREF " + Integer.toHexString(fromNaN(mStack[sp - 1]))); - Utils.log(" >>> DREF " + mStack[sp] + " " + mStack[sp - 1]); - int id = fromNaN(mStack[sp - 1]); - mStack[sp - 1] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp]); - return sp - 1; - }; - Op mA_MAX = - (sp) -> { // A_MAX - int id = fromNaN(mStack[sp]); - float[] array = mCollectionsAccess.getFloats(id); - float max = array[0]; - for (int i = 1; i < array.length; i++) { - max = Math.max(max, array[i]); - } - mStack[sp] = max; - return sp; - }; - Op mA_MIN = - (sp) -> { // A_MIN - int id = fromNaN(mStack[sp]); - float[] array = mCollectionsAccess.getFloats(id); - if (array.length == 0) { - return sp; - } - float min = array[0]; - for (int i = 1; i < array.length; i++) { - min = Math.min(min, array[i]); - } - mStack[sp] = min; - return sp; - }; - Op mA_SUM = - (sp) -> { // A_SUM - int id = fromNaN(mStack[sp]); - float[] array = mCollectionsAccess.getFloats(id); - float sum = 0; - for (int i = 0; i < array.length; i++) { - sum += array[i]; - } - mStack[sp] = sum; - return sp; - }; - Op mA_AVG = - (sp) -> { // A_AVG - int id = fromNaN(mStack[sp]); - float[] array = mCollectionsAccess.getFloats(id); - float sum = 0; - for (int i = 0; i < array.length; i++) { - sum += array[i]; - } - mStack[sp] = sum / array.length; - return sp; - }; - Op mA_LEN = - (sp) -> { // A_LEN - int id = fromNaN(mStack[sp]); - mStack[sp] = mCollectionsAccess.getListLength(id); - return sp; - }; - Op mA_SPLINE = - (sp) -> { // A_SPLINE - int id = fromNaN(mStack[sp - 1]); - mStack[sp - 1] = getSplineValue(id, mStack[sp]); - return sp - 1; - }; - Op mFIRST_VAR = - (sp) -> { // FIRST_VAR - mStack[sp] = mVar[0]; - return sp; - }; - Op mSECOND_VAR = - (sp) -> { // SECOND_VAR - mStack[sp] = mVar[1]; - return sp; - }; - Op mTHIRD_VAR = - (sp) -> { // THIRD_VAR - mStack[sp] = mVar[2]; - return sp; - }; - - Op[] ops = { - null, - mADD, - mSUB, - mMUL, - mDIV, - mMOD, - mMIN, - mMAX, - mPOW, - mSQRT, - mABS, - mSIGN, - mCOPY_SIGN, - mEXP, - mFLOOR, - mLOG, - mLN, - mROUND, - mSIN, - mCOS, - mTAN, - mASIN, - mACOS, - mATAN, - mATAN2, - mMAD, - mTERNARY_CONDITIONAL, - mCLAMP, - mCBRT, - mDEG, - mRAD, - mCEIL, - mA_DEREF, - mA_MAX, - mA_MIN, - mA_SUM, - mA_AVG, - mA_LEN, - mA_SPLINE, - mFIRST_VAR, - mSECOND_VAR, - mTHIRD_VAR, - }; - mOps = ops; - } - static { int k = 0; sNames.put(k++, "NOP"); @@ -765,4 +478,248 @@ public class AnimatedFloatExpression { int b = Float.floatToRawIntBits(v); return b & 0x7FFFFF; } + + // ================= New approach ======== + private static final int OP_ADD = OFFSET + 1; + private static final int OP_SUB = OFFSET + 2; + private static final int OP_MUL = OFFSET + 3; + private static final int OP_DIV = OFFSET + 4; + private static final int OP_MOD = OFFSET + 5; + private static final int OP_MIN = OFFSET + 6; + private static final int OP_MAX = OFFSET + 7; + private static final int OP_POW = OFFSET + 8; + private static final int OP_SQRT = OFFSET + 9; + private static final int OP_ABS = OFFSET + 10; + private static final int OP_SIGN = OFFSET + 11; + private static final int OP_COPY_SIGN = OFFSET + 12; + private static final int OP_EXP = OFFSET + 13; + private static final int OP_FLOOR = OFFSET + 14; + private static final int OP_LOG = OFFSET + 15; + private static final int OP_LN = OFFSET + 16; + private static final int OP_ROUND = OFFSET + 17; + private static final int OP_SIN = OFFSET + 18; + private static final int OP_COS = OFFSET + 19; + private static final int OP_TAN = OFFSET + 20; + private static final int OP_ASIN = OFFSET + 21; + private static final int OP_ACOS = OFFSET + 22; + private static final int OP_ATAN = OFFSET + 23; + private static final int OP_ATAN2 = OFFSET + 24; + private static final int OP_MAD = OFFSET + 25; + private static final int OP_TERNARY_CONDITIONAL = OFFSET + 26; + private static final int OP_CLAMP = OFFSET + 27; + private static final int OP_CBRT = OFFSET + 28; + private static final int OP_DEG = OFFSET + 29; + private static final int OP_RAD = OFFSET + 30; + private static final int OP_CEIL = OFFSET + 31; + private static final int OP_A_DEREF = OFFSET + 32; + private static final int OP_A_MAX = OFFSET + 33; + private static final int OP_A_MIN = OFFSET + 34; + private static final int OP_A_SUM = OFFSET + 35; + private static final int OP_A_AVG = OFFSET + 36; + private static final int OP_A_LEN = OFFSET + 37; + private static final int OP_A_SPLINE = OFFSET + 38; + private static final int OP_FIRST_VAR = OFFSET + 39; + private static final int OP_SECOND_VAR = OFFSET + 40; + private static final int OP_THIRD_VAR = OFFSET + 41; + + int opEval(int sp, int id) { + float[] array; + + switch (id) { + case OP_ADD: + mStack[sp - 1] = mStack[sp - 1] + mStack[sp]; + return sp - 1; + + case OP_SUB: + mStack[sp - 1] = mStack[sp - 1] - mStack[sp]; + return sp - 1; + + case OP_MUL: + mStack[sp - 1] = mStack[sp - 1] * mStack[sp]; + return sp - 1; + + case OP_DIV: + mStack[sp - 1] = mStack[sp - 1] / mStack[sp]; + return sp - 1; + + case OP_MOD: + mStack[sp - 1] = mStack[sp - 1] % mStack[sp]; + return sp - 1; + + case OP_MIN: + mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]); + return sp - 1; + + case OP_MAX: + mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]); + return sp - 1; + + case OP_POW: + mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]); + return sp - 1; + + case OP_SQRT: + mStack[sp] = (float) Math.sqrt(mStack[sp]); + return sp; + + case OP_ABS: + mStack[sp] = (float) Math.abs(mStack[sp]); + return sp; + + case OP_SIGN: + mStack[sp] = (float) Math.signum(mStack[sp]); + return sp; + + case OP_COPY_SIGN: + mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]); + return sp - 1; + + case OP_EXP: + mStack[sp] = (float) Math.exp(mStack[sp]); + return sp; + + case OP_FLOOR: + mStack[sp] = (float) Math.floor(mStack[sp]); + return sp; + + case OP_LOG: + mStack[sp] = (float) Math.log10(mStack[sp]); + return sp; + + case OP_LN: + mStack[sp] = (float) Math.log(mStack[sp]); + return sp; + + case OP_ROUND: + mStack[sp] = (float) Math.round(mStack[sp]); + return sp; + + case OP_SIN: + mStack[sp] = (float) Math.sin(mStack[sp]); + return sp; + + case OP_COS: + mStack[sp] = (float) Math.cos(mStack[sp]); + return sp; + + case OP_TAN: + mStack[sp] = (float) Math.tan(mStack[sp]); + return sp; + + case OP_ASIN: + mStack[sp] = (float) Math.asin(mStack[sp]); + return sp; + + case OP_ACOS: + mStack[sp] = (float) Math.acos(mStack[sp]); + return sp; + + case OP_ATAN: + mStack[sp] = (float) Math.atan(mStack[sp]); + return sp; + + case OP_ATAN2: + mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]); + return sp - 1; + + case OP_MAD: + mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2]; + return sp - 2; + + case OP_TERNARY_CONDITIONAL: + mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2]; + return sp - 2; + + case OP_CLAMP: + mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]); + return sp - 2; + + case OP_CBRT: + mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.); + return sp; + + case OP_DEG: + mStack[sp] = mStack[sp] * FP_TO_RAD; + return sp; + + case OP_RAD: + mStack[sp] = mStack[sp] * FP_TO_DEG; + return sp; + + case OP_CEIL: + mStack[sp] = (float) Math.ceil(mStack[sp]); + return sp; + + case OP_A_DEREF: + id = fromNaN(mStack[sp - 1]); + mStack[sp - 1] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp]); + return sp - 1; + + case OP_A_MAX: + id = fromNaN(mStack[sp]); + array = mCollectionsAccess.getFloats(id); + float max = array[0]; + for (int i = 1; i < array.length; i++) { + max = Math.max(max, array[i]); + } + mStack[sp] = max; + return sp; + + case OP_A_MIN: + id = fromNaN(mStack[sp]); + array = mCollectionsAccess.getFloats(id); + if (array.length == 0) { + return sp; + } + float min = array[0]; + for (int i = 1; i < array.length; i++) { + min = Math.min(min, array[i]); + } + mStack[sp] = min; + return sp; + + case OP_A_SUM: + id = fromNaN(mStack[sp]); + array = mCollectionsAccess.getFloats(id); + float sum = 0; + for (int i = 0; i < array.length; i++) { + sum += array[i]; + } + mStack[sp] = sum; + return sp; + + case OP_A_AVG: + id = fromNaN(mStack[sp]); + array = mCollectionsAccess.getFloats(id); + sum = 0; + for (int i = 0; i < array.length; i++) { + sum += array[i]; + } + mStack[sp] = sum / array.length; + return sp; + + case OP_A_LEN: + id = fromNaN(mStack[sp]); + mStack[sp] = mCollectionsAccess.getListLength(id); + return sp; + + case OP_A_SPLINE: + id = fromNaN(mStack[sp - 1]); + mStack[sp - 1] = getSplineValue(id, mStack[sp]); + return sp - 1; + + case OP_FIRST_VAR: + mStack[sp] = mVar[0]; + return sp; + + case OP_SECOND_VAR: + mStack[sp] = mVar[1]; + return sp; + + case OP_THIRD_VAR: + mStack[sp] = mVar[2]; + return sp; + } + return sp; + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java index 4f1287265d75..b92f96f63eb9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java @@ -24,13 +24,39 @@ import android.annotation.Nullable; public interface CollectionsAccess { float getFloatValue(int id, int index); + /** + * Get the array of float if it is a float array + * + * @param id the id of the float array + * @return + */ @Nullable float[] getFloats(int id); + /** + * Get the number of entries in the list + * + * @param id the id of the list + * @return + */ int getListLength(int id); + /** + * get the id of an entry if the list is a list of id's + * + * @param listId the list id + * @param index the index into the list + * @return + */ int getId(int listId, int index); + /** + * Get the value as an integer + * + * @param listId the list id to access + * @param index the index into the list + * @return + */ default int getIntValue(int listId, int index) { return (int) getFloatValue(listId, index); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java index f73ab39e496e..0a3351188d9e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java @@ -82,7 +82,7 @@ public class IntegerExpressionEvaluator { for (int i = 0; i < mStack.length; i++) { int v = mStack[i]; if (((1 << i) & mask) != 0) { - sp = mOps[v - OFFSET].eval(sp); + sp = opEval(sp, v); } else { mStack[++sp] = v; } @@ -107,7 +107,7 @@ public class IntegerExpressionEvaluator { for (int i = 0; i < len; i++) { int v = mStack[i]; if (((1 << i) & mask) != 0) { - sp = mOps[v - OFFSET].eval(sp); + sp = opEval(sp, v); } else { mStack[++sp] = v; } @@ -130,7 +130,7 @@ public class IntegerExpressionEvaluator { for (int i = 0; i < exp.length; i++) { int v = mStack[i]; if (((1 << i) & opMask) != 0) { - sp = mOps[v - OFFSET].eval(sp); + sp = opEval(sp, v); } else { mStack[++sp] = v; } @@ -138,171 +138,140 @@ public class IntegerExpressionEvaluator { return mStack[sp]; } - @NonNull Op[] mOps; - - { - Op mADD = - (sp) -> { // ADD - mStack[sp - 1] = mStack[sp - 1] + mStack[sp]; - return sp - 1; - }; - Op mSUB = - (sp) -> { // SUB - mStack[sp - 1] = mStack[sp - 1] - mStack[sp]; - return sp - 1; - }; - Op mMUL = - (sp) -> { // MUL - mStack[sp - 1] = mStack[sp - 1] * mStack[sp]; - return sp - 1; - }; - Op mDIV = - (sp) -> { // DIV - mStack[sp - 1] = mStack[sp - 1] / mStack[sp]; - return sp - 1; - }; - Op mMOD = - (sp) -> { // MOD - mStack[sp - 1] = mStack[sp - 1] % mStack[sp]; - return sp - 1; - }; - Op mSHL = - (sp) -> { // SHL - mStack[sp - 1] = mStack[sp - 1] << mStack[sp]; - return sp - 1; - }; - Op mSHR = - (sp) -> { // SHR - mStack[sp - 1] = mStack[sp - 1] >> mStack[sp]; - return sp - 1; - }; - Op mUSHR = - (sp) -> { // USHR - mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp]; - return sp - 1; - }; - Op mOR = - (sp) -> { // OR - mStack[sp - 1] = mStack[sp - 1] | mStack[sp]; - return sp - 1; - }; - Op mAND = - (sp) -> { // AND - mStack[sp - 1] = mStack[sp - 1] & mStack[sp]; - return sp - 1; - }; - Op mXOR = - (sp) -> { // XOR - mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp]; - return sp - 1; - }; - Op mCOPY_SIGN = - (sp) -> { // COPY_SIGN copy the sign via bit manipulation - mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31); - return sp - 1; - }; - Op mMIN = - (sp) -> { // MIN - mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]); - return sp - 1; - }; - Op mMAX = - (sp) -> { // MAX - mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]); - return sp - 1; - }; - Op mNEG = - (sp) -> { // NEG - mStack[sp] = -mStack[sp]; - return sp; - }; - Op mABS = - (sp) -> { // ABS - mStack[sp] = Math.abs(mStack[sp]); - return sp; - }; - Op mINCR = - (sp) -> { // INCR - mStack[sp] = mStack[sp] + 1; - return sp; - }; - Op mDECR = - (sp) -> { // DECR - mStack[sp] = mStack[sp] - 1; - return sp; - }; - Op mNOT = - (sp) -> { // NOT - mStack[sp] = ~mStack[sp]; - return sp; - }; - Op mSIGN = - (sp) -> { // SIGN x<0 = -1,x==0 = 0 , x>0 = 1 - mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31); - return sp; - }; - Op mCLAMP = - (sp) -> { // CLAMP(min,max, val) - mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]); - return sp - 2; - }; - Op mTERNARY_CONDITIONAL = - (sp) -> { // TERNARY_CONDITIONAL - mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2]; - return sp - 2; - }; - Op mMAD = - (sp) -> { // MAD - mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2]; - return sp - 2; - }; - Op mFIRST_VAR = - (sp) -> { // FIRST_VAR - mStack[sp] = mVar[0]; - return sp; - }; - Op mSECOND_VAR = - (sp) -> { // SECOND_VAR - mStack[sp] = mVar[1]; - return sp; - }; - Op mTHIRD_VAR = - (sp) -> { // THIRD_VAR - mStack[sp] = mVar[2]; - return sp; - }; - - Op[] ops = { - null, - mADD, - mSUB, - mMUL, - mDIV, - mMOD, - mSHL, - mSHR, - mUSHR, - mOR, - mAND, - mXOR, - mCOPY_SIGN, - mMIN, - mMAX, - mNEG, - mABS, - mINCR, - mDECR, - mNOT, - mSIGN, - mCLAMP, - mTERNARY_CONDITIONAL, - mMAD, - mFIRST_VAR, - mSECOND_VAR, - mTHIRD_VAR, - }; - - mOps = ops; + private static final int OP_ADD = OFFSET + 1; + private static final int OP_SUB = OFFSET + 2; + private static final int OP_MUL = OFFSET + 3; + private static final int OP_DIV = OFFSET + 4; + private static final int OP_MOD = OFFSET + 5; + private static final int OP_SHL = OFFSET + 6; + private static final int OP_SHR = OFFSET + 7; + private static final int OP_USHR = OFFSET + 8; + private static final int OP_OR = OFFSET + 9; + private static final int OP_AND = OFFSET + 10; + private static final int OP_XOR = OFFSET + 11; + private static final int OP_COPY_SIGN = OFFSET + 12; + private static final int OP_MIN = OFFSET + 13; + private static final int OP_MAX = OFFSET + 14; + private static final int OP_NEG = OFFSET + 15; + private static final int OP_ABS = OFFSET + 16; + private static final int OP_INCR = OFFSET + 17; + private static final int OP_DECR = OFFSET + 18; + private static final int OP_NOT = OFFSET + 19; + private static final int OP_SIGN = OFFSET + 20; + private static final int OP_CLAMP = OFFSET + 21; + private static final int OP_TERNARY_CONDITIONAL = OFFSET + 22; + private static final int OP_MAD = OFFSET + 23; + private static final int OP_FIRST_VAR = OFFSET + 24; + private static final int OP_SECOND_VAR = OFFSET + 25; + private static final int OP_THIRD_VAR = OFFSET + 26; + + int opEval(int sp, int id) { + switch (id) { + case OP_ADD: // ADD + mStack[sp - 1] = mStack[sp - 1] + mStack[sp]; + return sp - 1; + + case OP_SUB: // SUB + mStack[sp - 1] = mStack[sp - 1] - mStack[sp]; + return sp - 1; + + case OP_MUL: // MUL + mStack[sp - 1] = mStack[sp - 1] * mStack[sp]; + return sp - 1; + + case OP_DIV: // DIV + mStack[sp - 1] = mStack[sp - 1] / mStack[sp]; + return sp - 1; + + case OP_MOD: // MOD + mStack[sp - 1] = mStack[sp - 1] % mStack[sp]; + return sp - 1; + + case OP_SHL: // SHL + mStack[sp - 1] = mStack[sp - 1] << mStack[sp]; + return sp - 1; + + case OP_SHR: // SHR + mStack[sp - 1] = mStack[sp - 1] >> mStack[sp]; + return sp - 1; + + case OP_USHR: // USHR + mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp]; + return sp - 1; + + case OP_OR: // OR + mStack[sp - 1] = mStack[sp - 1] | mStack[sp]; + return sp - 1; + + case OP_AND: // AND + mStack[sp - 1] = mStack[sp - 1] & mStack[sp]; + return sp - 1; + + case OP_XOR: // XOR + mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp]; + return sp - 1; + + case OP_COPY_SIGN: // COPY_SIGN copy the sign via bit manipulation + mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31); + return sp - 1; + + case OP_MIN: // MIN + mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]); + return sp - 1; + + case OP_MAX: // MAX + mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]); + return sp - 1; + + case OP_NEG: // NEG + mStack[sp] = -mStack[sp]; + return sp; + + case OP_ABS: // ABS + mStack[sp] = Math.abs(mStack[sp]); + return sp; + + case OP_INCR: // INCR + mStack[sp] = mStack[sp] + 1; + return sp; + + case OP_DECR: // DECR + mStack[sp] = mStack[sp] - 1; + return sp; + + case OP_NOT: // NOT + mStack[sp] = ~mStack[sp]; + return sp; + + case OP_SIGN: // SIGN x<0 = -1,x==0 = 0 , x>0 = 1 + mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31); + return sp; + + case OP_CLAMP: // CLAMP(min,max, val) + mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]); + return sp - 2; + + case OP_TERNARY_CONDITIONAL: // TERNARY_CONDITIONAL + mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2]; + return sp - 2; + + case OP_MAD: // MAD + mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2]; + return sp - 2; + + case OP_FIRST_VAR: // FIRST_VAR + mStack[sp] = mVar[0]; + return sp; + + case OP_SECOND_VAR: // SECOND_VAR + mStack[sp] = mVar[1]; + return sp; + + case OP_THIRD_VAR: // THIRD_VAR + mStack[sp] = mVar[2]; + return sp; + } + return 0; } static { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java index 7e02bc9416a9..c7e2442221cd 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java @@ -31,6 +31,11 @@ package com.android.internal.widget.remotecompose.core.operations.utilities.touc * limitations under the License. */ +/** + * This computes an form of easing such that the values constrained to be consistent in velocity The + * easing function is also constrained by the configure To have: a maximum time to stop, a maximum + * velocity, a maximum acceleration + */ public class VelocityEasing { private float mStartPos = 0; private float mStartV = 0; @@ -46,6 +51,11 @@ public class VelocityEasing { private boolean mOneDimension = true; private float mTotalEasingDuration = 0; + /** + * get the duration the easing will take + * + * @return the duration for the easing + */ public float getDuration() { if (mEasing != null) { return mTotalEasingDuration; @@ -53,6 +63,12 @@ public class VelocityEasing { return mDuration; } + /** + * Get the velocity at time t + * + * @param t time in seconds + * @return the velocity units/second + */ public float getV(float t) { if (mEasing == null) { for (int i = 0; i < mNumberOfStages; i++) { @@ -71,6 +87,12 @@ public class VelocityEasing { return (float) getEasingDiff((t - mStage[lastStages].mStartTime)); } + /** + * Get the position t seconds after the configure + * + * @param t time in seconds + * @return the position at time t + */ public float getPos(float t) { if (mEasing == null) { for (int i = 0; i < mNumberOfStages; i++) { @@ -91,6 +113,7 @@ public class VelocityEasing { return ret; } + @Override public String toString() { var s = " "; for (int i = 0; i < mNumberOfStages; i++) { @@ -100,6 +123,17 @@ public class VelocityEasing { return s; } + /** + * Configure the Velocity easing curve The system is in arbitrary units + * + * @param currentPos the current position + * @param destination the destination + * @param currentVelocity the current velocity units/seconds + * @param maxTime the max time to achieve position + * @param maxAcceleration the max acceleration units/s^2 + * @param maxVelocity the maximum velocity + * @param easing End in using this easing curve + */ public void config( float currentPos, float destination, diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java index 4af79f3ce4f4..975213f76bd2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java @@ -29,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Used to represent a boolean */ -public class BooleanConstant implements Operation { +public class BooleanConstant extends Operation { private static final int OP_CODE = Operations.DATA_BOOLEAN; private boolean mValue = false; private int mId; @@ -73,6 +73,11 @@ public class BooleanConstant implements Operation { return "OrigamiBoolean"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.DATA_BOOLEAN; } @@ -90,6 +95,12 @@ public class BooleanConstant implements Operation { buffer.writeBoolean(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java index 613e7328e24a..210a15ac7ca4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java @@ -29,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Represents a single integer typically used for states or named for input into the system */ -public class IntegerConstant implements Operation { +public class IntegerConstant extends Operation { private int mValue = 0; private int mId; @@ -65,6 +65,11 @@ public class IntegerConstant implements Operation { return "IntegerConstant"; } + /** + * The OP_CODE for this command + * + * @return the opcode + */ public static int id() { return Operations.DATA_INT; } @@ -82,6 +87,12 @@ public class IntegerConstant implements Operation { buffer.writeInt(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java index 745caa384e4c..9875c935c112 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java @@ -29,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; /** Used to represent a long */ -public class LongConstant implements Operation { +public class LongConstant extends Operation { private static final int OP_CODE = Operations.DATA_LONG; private long mValue; private int mId; @@ -83,6 +83,12 @@ public class LongConstant implements Operation { buffer.writeLong(value); } + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { int id = buffer.readInt(); diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java index 648f7bf06dd4..19b4b36b504c 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java +++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java @@ -659,4 +659,14 @@ public class RemoteComposePlayer extends FrameLayout { } mListener = null; } + + /** + * This returns the amount of time in ms the player used to evalueate a pass it is averaged over + * a number of evaluations. + * + * @return time in ms + */ + public float getEvalTime() { + return mInner.getEvalTime(); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java index 3c91cffcec3f..bc7d5e108e1d 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java @@ -529,6 +529,7 @@ public class AndroidPaintContext extends PaintContext { @Override public void setImageFilterQuality(int quality) { Utils.log(" quality =" + quality); + mPaint.setFilterBitmap(quality == 1); } @Override @@ -711,20 +712,32 @@ public class AndroidPaintContext extends PaintContext { } @Override + public void tweenPath(int out, int path1, int path2, float tween) { + float[] p = getPathArray(path1, path2, tween); + AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext; + androidContext.mRemoteComposeState.putPathData(out, p); + } + + @Override public void reset() { mPaint.reset(); } private Path getPath(int path1Id, int path2Id, float tween, float start, float end) { + return getPath(getPathArray(path1Id, path2Id, tween), start, end); + } + + private float[] getPathArray(int path1Id, int path2Id, float tween) { + AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext; if (tween == 0.0f) { - return getPath(path1Id, start, end); + return androidContext.mRemoteComposeState.getPathData(path1Id); } if (tween == 1.0f) { - return getPath(path2Id, start, end); + return androidContext.mRemoteComposeState.getPathData(path2Id); } - AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext; - float[] data1 = (float[]) androidContext.mRemoteComposeState.getFromId(path1Id); - float[] data2 = (float[]) androidContext.mRemoteComposeState.getFromId(path2Id); + + float[] data1 = androidContext.mRemoteComposeState.getPathData(path1Id); + float[] data2 = androidContext.mRemoteComposeState.getPathData(path2Id); float[] tmp = new float[data2.length]; for (int i = 0; i < tmp.length; i++) { if (Float.isNaN(data1[i]) || Float.isNaN(data2[i])) { @@ -733,6 +746,10 @@ public class AndroidPaintContext extends PaintContext { tmp[i] = (data2[i] - data1[i]) * tween + data1[i]; } } + return tmp; + } + + private Path getPath(float[] tmp, float start, float end) { Path path = new Path(); FloatsToPath.genPath(path, tmp, start, end); return path; @@ -741,9 +758,9 @@ public class AndroidPaintContext extends PaintContext { private Path getPath(int id, float start, float end) { AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext; Path path = new Path(); - if (androidContext.mRemoteComposeState.containsId(id)) { - float[] data = (float[]) androidContext.mRemoteComposeState.getFromId(id); - FloatsToPath.genPath(path, data, start, end); + float[] pathData = androidContext.mRemoteComposeState.getPathData(id); + if (pathData != null) { + FloatsToPath.genPath(path, pathData, start, end); } return path; } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java index 77c25147b1fd..0fb0a28da1db 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java @@ -60,9 +60,12 @@ class AndroidRemoteContext extends RemoteContext { @Override public void loadPathData(int instanceId, @NonNull float[] floatPath) { - if (!mRemoteComposeState.containsId(instanceId)) { - mRemoteComposeState.cacheData(instanceId, floatPath); - } + mRemoteComposeState.putPathData(instanceId, floatPath); + } + + @Override + public float[] getPathData(int instanceId) { + return mRemoteComposeState.getPathData(instanceId); } static class VarName { @@ -162,7 +165,7 @@ class AndroidRemoteContext extends RemoteContext { * @param type the type of the data 0 = RGBA 8888, 1 = 888, 2 = 8 gray * @param width with of image to be loaded largest dimension is 32767 * @param height height of image to be loaded - * @param bitmap a byte array containing the image information + * @param data a byte array containing the image information * @oaram imageId the id of the image */ @Override diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java index 8f55f8abf713..ecfd13aa66b6 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java @@ -349,6 +349,27 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta private int mCount; private long mTime = System.nanoTime(); + private long mDuration; + private boolean mEvalTime = false; + + /** + * This returns the amount of time in ms the player used to evalueate a pass it is averaged over + * a number of evaluations. + * + * @return time in ms + */ + public float getEvalTime() { + if (!mEvalTime) { + mEvalTime = true; + return 0.0f; + } + double avg = mDuration / (double) mCount; + if (mCount > 100) { + mDuration /= 2; + mCount /= 2; + } + return (float) (avg * 1E-6); // ms + } @Override protected void onDraw(Canvas canvas) { @@ -356,6 +377,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta if (mDocument == null) { return; } + long start = mEvalTime ? System.nanoTime() : 0; mARContext.setAnimationEnabled(true); mARContext.currentTime = System.currentTimeMillis(); mARContext.setDebug(mDebug); @@ -376,5 +398,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta if (mDocument.needsRepaint() > 0) { invalidate(); } + if (mEvalTime) { + mDuration += System.nanoTime() - start; + mCount++; + } } } diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java index d05f5e3950b4..70dd10f2c371 100644 --- a/core/java/com/android/server/pm/pkg/AndroidPackage.java +++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java @@ -875,6 +875,14 @@ public interface AndroidPackage { int getMemtagMode(); /** + * @see ApplicationInfo#getPageSizeAppCompatFlags() + * @see R.styleable#AndroidManifestApplication_pageSizeCompat + * @hide + */ + @ApplicationInfo.PageSizeAppCompatFlags + int getPageSizeAppCompatFlags(); + + /** * TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?) * @see R.styleable#AndroidManifestMetaData * @hide diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 5c03c5cca66d..e22d9587093b 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -214,6 +214,7 @@ cc_library_shared_for_libandroid_runtime { "android_media_ToneGenerator.cpp", "android_hardware_Camera.cpp", "android_hardware_camera2_CameraMetadata.cpp", + "android_hardware_camera2_CameraDevice.cpp", "android_hardware_camera2_DngCreator.cpp", "android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp", "android_hardware_camera2_utils_SurfaceUtils.cpp", @@ -304,7 +305,12 @@ cc_library_shared_for_libandroid_runtime { "av-types-aidl-cpp", "android.hardware.camera.device@3.2", "camera_platform_flags_c_lib", + "android.hardware.common.fmq-V1-cpp", + "android.hardware.common-V2-cpp", + "android.hardware.common.fmq-V1-ndk", + "android.hardware.common-V2-ndk", "libandroid_net", + "libfmq", "libbattery", "libnetdutils", "libmemtrack", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 00a62977de43..ac187b08f0f1 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -77,6 +77,7 @@ extern int register_android_opengl_jni_GLES32(JNIEnv* env); extern int register_android_hardware_Camera(JNIEnv *env); extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env); +extern int register_android_hardware_camera2_CameraDevice(JNIEnv *env); extern int register_android_hardware_camera2_DngCreator(JNIEnv *env); extern int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env); extern int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env); @@ -1623,6 +1624,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_util_VirtualRefBasePtr), REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_camera2_CameraMetadata), + REG_JNI(register_android_hardware_camera2_CameraDevice), REG_JNI(register_android_hardware_camera2_DngCreator), REG_JNI(register_android_hardware_camera2_impl_CameraExtensionJpegProcessor), REG_JNI(register_android_hardware_camera2_utils_SurfaceUtils), diff --git a/core/jni/android_hardware_camera2_CameraDevice.cpp b/core/jni/android_hardware_camera2_CameraDevice.cpp new file mode 100644 index 000000000000..493c7073416c --- /dev/null +++ b/core/jni/android_hardware_camera2_CameraDevice.cpp @@ -0,0 +1,148 @@ +/* +** +** Copyright 2024, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// #define LOG_NDEBUG 0 + +#define ATRACE_TAG ATRACE_TAG_CAMERA + +#include <memory> +#define LOG_TAG "CameraDevice-JNI" +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/Trace.h> +#include <vector> + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include "android_os_Parcel.h" +#include "core_jni_helpers.h" +#include <android/binder_parcel_jni.h> +#include <android/hardware/camera2/ICameraDeviceUser.h> +#include <aidl/android/hardware/common/fmq/MQDescriptor.h> +#include <aidl/android/hardware/common/fmq/SynchronizedReadWrite.h> +#include <fmq/AidlMessageQueue.h> +#include <camera/CameraMetadata.h> + +using namespace android; + +using ::android::AidlMessageQueue; +using ResultMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>; + +class FMQReader { + public: + FMQReader(MQDescriptor<int8_t, SynchronizedReadWrite> &resultMQ) { + mCaptureResultMetadataQueue = std::make_shared<ResultMetadataQueue>(resultMQ); + } + std::shared_ptr<CameraMetadata> readOneResultMetadata(long metadataSize); + private: + std::shared_ptr<ResultMetadataQueue> mCaptureResultMetadataQueue = nullptr; +}; + +std::shared_ptr<CameraMetadata> FMQReader::readOneResultMetadata(long metadataSize) { + ATRACE_CALL(); + if (metadataSize == 0) { + return nullptr; + } + auto metadataVec = std::make_unique<int8_t []>(metadataSize); + bool read = mCaptureResultMetadataQueue->read(metadataVec.get(), metadataSize); + if (!read) { + ALOGE("%s capture metadata could't be read from fmq", __FUNCTION__); + return nullptr; + } + + // Takes ownership of metadataVec, this doesn't copy + std::shared_ptr<CameraMetadata> retVal = + std::make_shared<CameraMetadata>( + reinterpret_cast<camera_metadata_t *>(metadataVec.release())); + return retVal; +} + +extern "C" { + +static jlong CameraDevice_createFMQReader(JNIEnv *env, jclass thiz, + jobject resultParcel) { + AParcel *resultAParcel = AParcel_fromJavaParcel(env, resultParcel); + if (resultAParcel == nullptr) { + ALOGE("%s: Error creating result parcel", __FUNCTION__); + return 0; + } + AParcel_setDataPosition(resultAParcel, 0); + + MQDescriptor<int8_t, SynchronizedReadWrite> resultMQ; + if (resultMQ.readFromParcel(resultAParcel) != OK) { + ALOGE("%s: read from result parcel failed", __FUNCTION__); + return 0; + } + return reinterpret_cast<jlong>(new std::shared_ptr<FMQReader>( + new FMQReader(resultMQ))); +} + +static std::shared_ptr<FMQReader>* FMQReader_getSharedPtr(jlong fmqReaderLongPtr) { + return reinterpret_cast<std::shared_ptr<FMQReader>* >(fmqReaderLongPtr); +} + +static jlong CameraDevice_readResultMetadata(JNIEnv *env, jclass thiz, jlong ptr, + jlong metadataSize) { + ALOGV("%s", __FUNCTION__); + + FMQReader *fmqReader = FMQReader_getSharedPtr(ptr)->get(); + auto metadataSp = fmqReader->readOneResultMetadata(metadataSize); + auto retVal = new std::shared_ptr<CameraMetadata>(metadataSp); + return reinterpret_cast<jlong>(retVal); +} + +static void CameraDevice_close(JNIEnv *env, jclass thiz, jlong ptr) { + ALOGV("%s", __FUNCTION__); + + auto fmqPtr = FMQReader_getSharedPtr(ptr); + if (fmqPtr != nullptr) { + delete fmqPtr; + } +} + +} + +//------------------------------------------------- +#define CAMERA_DEVICE_CLASS_NAME "android/hardware/camera2/impl/CameraDeviceImpl" +static const JNINativeMethod gCameraDeviceMethods[] = { +// static methods + { "nativeCreateFMQReader", + "(Landroid/os/Parcel;)J", + (void *)CameraDevice_createFMQReader}, + { "nativeReadResultMetadata", + "(JJ)J", + (void *)CameraDevice_readResultMetadata}, + { "nativeClose", + "(J)V", + (void*)CameraDevice_close}, +// instance methods +}; + + +// Get all the required offsets in java class and register native functions +int register_android_hardware_camera2_CameraDevice(JNIEnv *env) +{ + // Register native functions + return RegisterMethodsOrDie(env, + CAMERA_DEVICE_CLASS_NAME, + gCameraDeviceMethods, + NELEM(gCameraDeviceMethods)); +} + +extern "C" { + +} // extern "C"
\ No newline at end of file diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto index b7408a4da381..facadeedd1f8 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -114,6 +114,7 @@ message ApplicationInfoProto { optional int32 enable_memtag = 20; optional bool native_heap_zero_init = 21; optional bool allow_cross_uid_activity_switch_from_below = 22; + optional int32 enable_page_size_app_compat = 23; } optional Detail detail = 17; repeated string overlay_paths = 18; diff --git a/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml b/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml new file mode 100644 index 000000000000..b25adaabf8e8 --- /dev/null +++ b/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations underthe License + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/conversation_face_pile" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:forceHasOverlappingRendering="false" + > + <ImageView + android:id="@+id/conversation_face_pile_top" + android:layout_width="@dimen/messaging_avatar_size" + android:layout_height="@dimen/messaging_avatar_size" + android:scaleType="centerCrop" + android:layout_gravity="end|top" + android:background="@drawable/notification_icon_circle" + android:clipToOutline="true" + /> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="start|bottom"> + <ImageView + android:id="@+id/conversation_face_pile_bottom_background" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/conversation_badge_background" + /> + <ImageView + android:id="@+id/conversation_face_pile_bottom" + android:layout_width="@dimen/messaging_avatar_size" + android:layout_height="@dimen/messaging_avatar_size" + android:scaleType="centerCrop" + android:layout_gravity="center" + android:background="@drawable/notification_icon_circle" + android:clipToOutline="true" + /> + </FrameLayout> +</FrameLayout> diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml new file mode 100644 index 000000000000..90befd911bdf --- /dev/null +++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/conversation_icon_container" + android:layout_width="@dimen/conversation_content_start" + android:layout_height="wrap_content" + android:gravity="start|top" + android:clipChildren="false" + android:clipToPadding="false" + android:paddingTop="20dp" + android:paddingBottom="16dp" + android:importantForAccessibility="no" + > + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_gravity="top|center_horizontal" + > + + <!-- Big icon: 48x48, 12dp padding top, 16dp padding sides --> + <com.android.internal.widget.CachingIconView + android:id="@+id/conversation_icon" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:layout_marginLeft="@dimen/conversation_badge_protrusion" + android:layout_marginRight="@dimen/conversation_badge_protrusion" + android:layout_marginBottom="@dimen/conversation_badge_protrusion" + android:background="@drawable/notification_icon_circle" + android:clipToOutline="true" + android:scaleType="centerCrop" + android:importantForAccessibility="no" + /> + + <ViewStub + android:layout="@layout/notification_2025_conversation_face_pile_layout" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:layout_marginLeft="@dimen/conversation_badge_protrusion" + android:layout_marginRight="@dimen/conversation_badge_protrusion" + android:layout_marginBottom="@dimen/conversation_badge_protrusion" + android:id="@+id/conversation_face_pile" + /> + + <FrameLayout + android:id="@+id/conversation_icon_badge" + android:layout_width="@dimen/conversation_icon_size_badged" + android:layout_height="@dimen/conversation_icon_size_badged" + android:layout_gravity="end|bottom" + android:clipChildren="false" + android:clipToPadding="false" + > + + <com.android.internal.widget.CachingIconView + android:id="@+id/conversation_icon_badge_bg" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:src="@drawable/conversation_badge_background" + android:forceHasOverlappingRendering="false" + android:scaleType="center" + /> + + <com.android.internal.widget.NotificationRowIconView + android:id="@+id/icon" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + android:layout_gravity="center" + android:forceHasOverlappingRendering="false" + /> + + <com.android.internal.widget.CachingIconView + android:id="@+id/conversation_icon_badge_ring" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:src="@drawable/conversation_badge_ring" + android:visibility="gone" + android:forceHasOverlappingRendering="false" + android:clipToPadding="false" + android:scaleType="center" + /> + </FrameLayout> + </FrameLayout> +</FrameLayout> diff --git a/core/res/res/layout/notification_2025_messaging_group.xml b/core/res/res/layout/notification_2025_messaging_group.xml new file mode 100644 index 000000000000..c1b491fc4b0e --- /dev/null +++ b/core/res/res/layout/notification_2025_messaging_group.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- extends LinearLayout --> +<com.android.internal.widget.MessagingGroup + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" > + <FrameLayout + android:id="@+id/message_icon_container" + android:layout_width="@dimen/conversation_content_start" + android:layout_height="wrap_content"> + <ImageView + android:layout_gravity="top|center_horizontal" + android:id="@+id/message_icon" + android:layout_width="@dimen/messaging_avatar_size" + android:layout_height="@dimen/messaging_avatar_size" + android:background="@drawable/notification_icon_circle" + android:clipToOutline="true" + android:scaleType="centerCrop" + android:importantForAccessibility="no" /> + </FrameLayout> + <com.android.internal.widget.RemeasuringLinearLayout + android:id="@+id/messaging_group_content_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:baselineAligned="true" + android:orientation="vertical"> + <com.android.internal.widget.ImageFloatingTextView + android:id="@+id/message_name" + style="@style/Widget.DeviceDefault.Notification.MessagingName" + android:layout_width="wrap_content" + android:textAlignment="viewStart" + /> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/group_message_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_text_margin_top" + android:spacing="2dp" /> + </com.android.internal.widget.RemeasuringLinearLayout> + <FrameLayout + android:id="@+id/messaging_group_icon_container" + android:layout_width="@dimen/messaging_avatar_size" + android:layout_height="@dimen/messaging_avatar_size" + android:layout_marginStart="12dp" + android:visibility="gone"/> + <FrameLayout + android:id="@+id/messaging_group_sending_progress_container" + android:layout_width="@dimen/messaging_group_sending_progress_size" + android:layout_height="@dimen/messaging_avatar_size" + android:layout_marginStart="12dp" + android:layout_gravity="top" + android:visibility="gone"> + <ProgressBar + android:id="@+id/messaging_group_sending_progress" + android:layout_height="@dimen/messaging_group_sending_progress_size" + android:layout_width="@dimen/messaging_group_sending_progress_size" + android:layout_gravity="center" + android:indeterminate="true" + style="?android:attr/progressBarStyleSmall" /> + </FrameLayout> +</com.android.internal.widget.MessagingGroup> diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml index 4eac6d991272..a790e5da20de 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_base.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml @@ -123,7 +123,7 @@ <!-- This is the simplest way to keep this text vertically centered without gravity="center_vertical" which causes jumpiness in expansion animations. --> <include - layout="@layout/notification_2025_template_text" + layout="@layout/notification_2025_text" android:layout_width="match_parent" android:layout_height="@dimen/notification_text_height" android:layout_gravity="center_vertical" diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml new file mode 100644 index 000000000000..06f5f0651452 --- /dev/null +++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Extends FrameLayout --> +<com.android.internal.widget.CallLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:tag="call" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts --> + <include layout="@layout/notification_2025_conversation_icon_container" /> + + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="88dp" + android:orientation="horizontal" + > + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginStart="@dimen/conversation_content_start" + android:orientation="vertical" + android:minHeight="68dp" + android:paddingBottom="@dimen/notification_headerless_margin_twoline" + > + + <include + layout="@layout/notification_template_conversation_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + + <include layout="@layout/notification_template_text" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_text_height" + /> + + </LinearLayout> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + + <include + layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + /> + + </FrameLayout> + + </LinearLayout> + +</com.android.internal.widget.CallLayout> diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml new file mode 100644 index 000000000000..427c4e42e40c --- /dev/null +++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml @@ -0,0 +1,197 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2014 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Note: This is the old media style notification (different from UMO). --> + +<!-- extends FrameLayout --> +<com.android.internal.widget.MediaNotificationView + android:id="@+id/status_bar_latest_event_content" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_min_height" + android:tag="media" + > + + + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_2025_left_icon_size" + android:layout_height="@dimen/notification_2025_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + + <com.android.internal.widget.NotificationRowIconView + android:id="@+id/icon" + android:layout_width="@dimen/notification_2025_icon_circle_size" + android:layout_height="@dimen/notification_2025_icon_circle_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_icon_circle_start" + android:background="@drawable/notification_icon_circle" + android:padding="@dimen/notification_2025_icon_circle_padding" + /> + + <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_2025_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + android:focusable="false" + /> + + <LinearLayout + android:id="@+id/notification_headerless_view_row" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:orientation="horizontal" + > + + <LinearLayout + android:id="@+id/notification_headerless_view_column" + android:layout_width="0px" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:layout_marginBottom="@dimen/notification_headerless_margin_twoline" + android:layout_marginTop="@dimen/notification_headerless_margin_twoline" + android:orientation="vertical" + > + + <NotificationTopLineView + android:id="@+id/notification_top_line" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_headerless_line_height" + android:clipChildren="false" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- + NOTE: The notification_top_line_views layout contains the app_name_text. + In order to include the title view at the beginning, the Notification.Builder + has logic to hide that view whenever this title view is to be visible. + --> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:ellipsize="end" + android:fadingEdge="horizontal" + android:singleLine="true" + android:textAlignment="viewStart" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + /> + + <include layout="@layout/notification_top_line_views" /> + + </NotificationTopLineView> + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + > + + <com.android.internal.widget.NotificationVanishingFrameLayout + android:layout_width="match_parent" + android:layout_height="@dimen/notification_headerless_line_height" + > + <!-- This is the simplest way to keep this text vertically centered without + gravity="center_vertical" which causes jumpiness in expansion animations. --> + <include + layout="@layout/notification_template_text" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_text_height" + android:layout_gravity="center_vertical" + android:layout_marginTop="0dp" + /> + </com.android.internal.widget.NotificationVanishingFrameLayout> + + <include + layout="@layout/notification_template_progress" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_headerless_line_height" + /> + + </LinearLayout> + + </LinearLayout> + + <ImageView + android:id="@+id/right_icon" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + /> + + <LinearLayout + android:id="@+id/media_actions" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layoutDirection="ltr" + android:orientation="horizontal" + > + <include + layout="@layout/notification_material_media_action" + android:id="@+id/action0" + /> + <include + layout="@layout/notification_material_media_action" + android:id="@+id/action1" + /> + <include + layout="@layout/notification_material_media_action" + android:id="@+id/action2" + /> + </LinearLayout> + + <FrameLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + /> + + </FrameLayout> + + </LinearLayout> +</com.android.internal.widget.MediaNotificationView> diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml new file mode 100644 index 000000000000..f0e4c0f272fb --- /dev/null +++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml @@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Note: This is the old "Messaging Style" notification (not a conversation). --> + +<!-- extends FrameLayout --> +<com.android.internal.widget.MessagingLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:tag="messaging" + > + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:orientation="vertical" + > + + + <com.android.internal.widget.NotificationMaxHeightFrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_min_height" + android:clipChildren="false" + > + + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_2025_left_icon_size" + android:layout_height="@dimen/notification_2025_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + + <com.android.internal.widget.NotificationRowIconView + android:id="@+id/icon" + android:layout_width="@dimen/notification_2025_icon_circle_size" + android:layout_height="@dimen/notification_2025_icon_circle_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_icon_circle_start" + android:background="@drawable/notification_icon_circle" + android:padding="@dimen/notification_2025_icon_circle_padding" + /> + + <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_2025_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + android:focusable="false" + /> + + <!-- + NOTE: to make the expansion animation of id/notification_messaging happen vertically, + its X positioning must be the left edge of the notification, so instead of putting the + layout_marginStart on the id/notification_headerless_view_row, we put it on + id/notification_top_line, making the layout here just a bit different from the base. + --> + <LinearLayout + android:id="@+id/notification_headerless_view_row" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipChildren="false" + > + + <!-- + NOTE: because messaging will always have 2 lines, this LinearLayout should NOT + have the id/notification_headerless_view_column, as that is used for modifying + vertical margins to accommodate the single-line state that base supports + --> + <LinearLayout + android:layout_width="0px" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:layout_marginBottom="@dimen/notification_headerless_margin_twoline" + android:layout_marginTop="@dimen/notification_headerless_margin_twoline" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:clipChildren="false" + android:orientation="vertical" + > + + <NotificationTopLineView + android:id="@+id/notification_top_line" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_headerless_line_height" + android:clipChildren="false" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- + NOTE: The notification_top_line_views layout contains the app_name_text. + In order to include the title view at the beginning, the Notification.Builder + has logic to hide that view whenever this title view is to be visible. + --> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:ellipsize="end" + android:fadingEdge="horizontal" + android:singleLine="true" + android:textAlignment="viewStart" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + /> + + <include layout="@layout/notification_top_line_views" /> + + </NotificationTopLineView> + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:clipChildren="false" + > + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/notification_messaging" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:spacing="@dimen/notification_messaging_spacing" /> + </LinearLayout> + + </LinearLayout> + + <!-- Images --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/conversation_image_message_container" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:forceHasOverlappingRendering="false" + android:spacing="0dp" + android:clipChildren="false" + android:visibility="gone" + /> + + <ImageView + android:id="@+id/right_icon" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + /> + + <FrameLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + /> + + </FrameLayout> + + </LinearLayout> + + </com.android.internal.widget.NotificationMaxHeightFrameLayout> + + <LinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="-20dp" + android:clipChildren="false" + android:orientation="vertical"> + <include layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_content_margin" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" /> + <include layout="@layout/notification_material_action_list" /> + </LinearLayout> +</LinearLayout> +</com.android.internal.widget.MessagingLayout> diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml new file mode 100644 index 000000000000..0c4c7fba90b1 --- /dev/null +++ b/core/res/res/layout/notification_2025_template_conversation.xml @@ -0,0 +1,159 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- extends FrameLayout --> +<com.android.internal.widget.ConversationLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:tag="conversation" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <include layout="@layout/notification_2025_conversation_icon_container" /> + + <!-- Wraps entire "expandable" notification --> + <com.android.internal.widget.RemeasuringLinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:clipToPadding="false" + android:clipChildren="false" + android:orientation="vertical" + > + <!-- LinearLayout for Expand Button--> + <com.android.internal.widget.RemeasuringLinearLayout + android:id="@+id/expand_button_and_content_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="start|top" + android:orientation="horizontal" + android:clipChildren="false" + android:clipToPadding="false"> + <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc--> + <com.android.internal.widget.RemeasuringLinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="0dp" + android:orientation="vertical" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <!-- Header --> + + <!-- Use layout_marginStart instead of paddingStart to work around strange + measurement behavior on lower display densities. --> + <include + layout="@layout/notification_template_conversation_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="2dp" + android:layout_marginStart="@dimen/conversation_content_start" + /> + + <!-- Messages --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/notification_messaging" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_text_size" + android:spacing="@dimen/notification_messaging_spacing" + android:clipToPadding="false" + android:clipChildren="false" + /> + </com.android.internal.widget.RemeasuringLinearLayout> + + <!-- This is where the expand button container will be placed when collapsed--> + </com.android.internal.widget.RemeasuringLinearLayout> + + <include layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_content_margin" + android:layout_marginStart="@dimen/conversation_content_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" /> + <include layout="@layout/notification_material_action_list" /> + </com.android.internal.widget.RemeasuringLinearLayout> + + <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded. + The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to + its sibling view in accessibility focus order. + {see android.view.ViewGroup.addChildrenForAccessibility()} + expand_button_container will be moved under expand_button_and_content_container when collapsed, + this dynamic movement ensures message can flow under expand button when expanded--> + <FrameLayout + android:id="@+id/expand_button_a11y_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="end|top" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_marginTop="-1px" + android:paddingTop="1px" + > + <!--expand_button_container is dynamically placed between here and at the end of the + layout. It starts here since only FrameLayout layout params have gravity--> + <LinearLayout + android:id="@+id/expand_button_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="end|top" + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="vertical"> + <include layout="@layout/notification_close_button" + android:layout_width="@dimen/notification_close_button_size" + android:layout_height="@dimen/notification_close_button_size" + android:layout_gravity="end" + android:layout_marginEnd="20dp" + /> + <!--expand_button_touch_container makes sure that we can nicely center the expand + content in the collapsed layout while the parent makes sure that we're never laid out + bigger than the messaging content.--> + <LinearLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="@dimen/conversation_expand_button_height" + android:orientation="horizontal" + android:layout_gravity="end|top" + android:paddingEnd="0dp" + android:clipToPadding="false" + android:clipChildren="false" + > + <!-- Images --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/conversation_image_message_container" + android:forceHasOverlappingRendering="false" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_marginStart="@dimen/conversation_image_start_margin" + android:spacing="0dp" + android:layout_gravity="center" + android:clipToPadding="false" + android:clipChildren="false" + /> + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + /> + </LinearLayout> + </LinearLayout> + </FrameLayout> +</com.android.internal.widget.ConversationLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml new file mode 100644 index 000000000000..3ff71b78835d --- /dev/null +++ b/core/res/res/layout/notification_2025_template_expanded_call.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Extends FrameLayout --> +<com.android.internal.widget.CallLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:tag="call" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts --> + <include layout="@layout/notification_2025_conversation_icon_container" /> + + <LinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/notification_content_margin" + android:orientation="vertical" + > + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="horizontal" + > + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginStart="@dimen/conversation_content_start" + android:orientation="vertical" + android:minHeight="68dp" + > + + <include + layout="@layout/notification_template_conversation_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + + <include layout="@layout/notification_template_text_multiline" /> + + <include + android:layout_width="match_parent" + android:layout_height="@dimen/notification_progress_bar_height" + android:layout_marginTop="@dimen/notification_progress_margin_top" + layout="@layout/notification_template_progress" + /> + </LinearLayout> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + + <include + layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + + </FrameLayout> + + </LinearLayout> + + <ViewStub + android:layout="@layout/notification_material_reply_text" + android:id="@+id/notification_material_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + + <include + layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/notification_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:layout_marginTop="@dimen/notification_content_margin" + /> + + <include layout="@layout/notification_material_action_list" /> + + </LinearLayout> + +</com.android.internal.widget.CallLayout> diff --git a/core/res/res/layout/notification_2025_template_text.xml b/core/res/res/layout/notification_2025_text.xml index 48b1083a5e53..48b1083a5e53 100644 --- a/core/res/res/layout/notification_2025_template_text.xml +++ b/core/res/res/layout/notification_2025_text.xml diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml index 78299ab0ea99..7bb6fcfa2ae0 100644 --- a/core/res/res/layout/side_fps_toast.xml +++ b/core/res/res/layout/side_fps_toast.xml @@ -25,7 +25,7 @@ android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="6" - android:paddingBottom="10dp" + android:paddingBottom="16dp" android:text="@string/fp_power_button_enrollment_title" android:textColor="@color/side_fps_text_color" android:paddingLeft="20dp"/> @@ -37,7 +37,7 @@ android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="3" - android:paddingBottom="10dp" + android:paddingBottom="16dp" android:text="@string/fp_power_button_enrollment_button_text" style="?android:attr/buttonBarNegativeButtonStyle" android:textColor="@color/side_fps_button_color" diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index a4499efae37d..b0545eba0042 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> tot <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Enige kalender"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> demp sekere klanke"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Daar is \'n interne probleem met jou toestel en dit sal dalk onstabiel wees totdat jy \'n fabriekterugstelling doen."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 225f04d53ad0..e56f79afbe92 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"፣ "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"ከ<xliff:g id="START">%1$s</xliff:g> እስከ <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ማንኛውም ቀን መቁጠሪያ"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> አንዳንድ ድምጾችን እየዘጋ ነው"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"መሣሪያዎ ላይ የውስጣዊ ችግር አለ፣ የፋብሪካ ውሂብ ዳግም እስኪያስጀምሩት ድረስ ላይረጋጋ ይችላል።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 484d22047fcf..ebffb4da6e50 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1951,6 +1951,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"من <xliff:g id="START">%1$s</xliff:g> إلى <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"من <xliff:g id="START">%1$s</xliff:g> إلى <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"أي تقويم"</string> <string name="muted_by" msgid="91464083490094950">"يعمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> على كتم بعض الأصوات."</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط على الإعدادات الأصلية."</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index fdddc70e56d3..f80352ee6c6f 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>ৰ পৰা <xliff:g id="END">%2$s</xliff:g>লৈ"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"যিকোনো কেলেণ্ডাৰ"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>এ কিছুমান ধ্বনি মিউট কৰি আছে"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে আৰু আপুনি ফেক্টৰী ডেটা ৰিছেট নকৰালৈকে ই সুস্থিৰভাৱে কাম নকৰিব পাৰে।"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 003c0dbc6f26..57e2ddd41cdf 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"İstənilən təqvim"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bəzi səsləri səssiz rejimə salır"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızın daxili problemi var və istehsalçı sıfırlanması olmayana qədər qeyri-stabil ola 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 5a848bafc7eb..26ded97f11dc 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvuke"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Došlo je do internog problema u vezi sa uređajem i možda će biti nestabilan dok ne obavite resetovanje na fabrička podešavanja."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index ce98a5ff7473..4d8543731125 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Любы каляндар"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> выключае некаторыя гукі"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"На вашай прыладзе ўзнікла ўнутраная праблема, і яна можа працаваць нестабільна, пакуль вы не зробіце скід да заводскіх налад."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index aad5d48f80cd..226447ab7c74 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"От <xliff:g id="START">%1$s</xliff:g> до <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Всички календари"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> заглушава някои звуци"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Възникна вътрешен проблем с устройството ви. То може да е нестабилно, докато не възстановите фабричните настройки."</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index d52f6e91f45b..521b0926e97f 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> থেকে <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"যেকোনও ক্যালেন্ডার"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> কিছু সাউন্ডকে মিউট করে দিচ্ছে"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে, এবং আপনি যতক্ষণ না পর্যন্ত এটিকে ফ্যাক্টরি ডেটা রিসেট করছেন ততক্ষণ এটি ঠিকভাবে কাজ নাও করতে পারে৷"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index b74b13966405..e22d838ec000 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Postoji problem u vašem uređaju i može biti nestabilan dok ga ne vratite na fabričke postavke."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 7500122e6d94..6b3c9bf21ea0 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualsevol calendari"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> està silenciant alguns sons"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"S\'ha produït un error intern al dispositiu i és possible que funcioni de manera inestable fins que restableixis les dades de fàbrica."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 277d359e5fd0..b0760fee8506 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"V libovolném kalendáři"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypíná určité zvuky"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"V zařízení došlo k internímu problému. Dokud neprovedete obnovení továrních dat, může být nestabilní."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 9db6076ee6fa..f4fa796268b4 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> til <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle kalendere"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår nogle lyde fra"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Der er et internt problem med enheden, og den vil muligvis være ustabil, indtil du gendanner fabriksdataene."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 9561b3f8a3c5..5bf7933331f1 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> bis <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle Kalender"</string> <string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 551fe701e7dd..8289ff77ffb1 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> έως <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Οποιοδήποτε ημερολόγιο"</string> <string name="muted_by" msgid="91464083490094950">"Το τρίτο μέρος <xliff:g id="THIRD_PARTY">%1$s</xliff:g> θέτει ορισμένους ήχους σε σίγαση"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Υπάρχει ένα εσωτερικό πρόβλημα με τη συσκευή σας και ενδέχεται να είναι ασταθής μέχρι την επαναφορά των εργοστασιακών ρυθμίσεων."</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 5cedb1fa6655..0390acffcd62 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index b09a79a63039..5373b1ee5437 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 4e728ff0f814..d4bdad02f36e 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 578e4795f2b4..3eda31b6fd6d 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index da3c4c4c27eb..de3945a90736 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Cualquier calendario"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Existe un problema interno con el dispositivo, de modo que el dispositivo puede estar inestable hasta que restablezcas la configuración de fábrica."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 37bf70e9bbd1..e9ac320ab888 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Cualquier calendario"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Se ha producido un problema interno en el dispositivo y es posible que este no sea estable hasta que restablezcas el estado de fábrica."</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 0d79e8aa0333..69a3dd36cdc9 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> kuni <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Mis tahes kalender"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vaigistab teatud helid"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Seadmes ilmnes sisemine probleem ja seade võib olla ebastabiilne seni, kuni lähtestate seadme tehase andmetele."</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index b96f9eb97c21..e4f4bbd590de 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Edozein egutegi"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> soinu batzuk isilarazten ari da"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Barneko arazo bat dago zure gailuan eta agian ezegonkor egongo da jatorrizko datuak berrezartzen dituzun arte."</string> @@ -2438,7 +2440,7 @@ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satelite bidezko SOS komunikazioa ez da bateragarria"</string> <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satelite bidezko SOS komunikazioa ez da bateragarria gailu honekin"</string> <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satelite bidezko SOS komunikazioa ez dago konfiguratuta"</string> - <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Ziurtatu Internetera konektatuta zaudela eta saiatu konfiguratzen berriro"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Ziurtatu Internetera konektatuta zaudela eta saiatu berriro konfiguratzen"</string> <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satelite bidezko SOS komunikazioa ez dago erabilgarri"</string> <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satelite bidezko SOS komunikazioa ez dago erabilgarri herrialde edo lurralde honetan"</string> <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satelite bidezko SOS komunikazioa ez dago konfiguratuta"</string> @@ -2450,7 +2452,7 @@ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satelite bidezko mezularitza ez da bateragarria"</string> <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satelite bidezko mezularitza ez da bateragarria gailu honekin"</string> <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satelite bidezko mezularitza ez dago konfiguratuta"</string> - <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Ziurtatu Internetera konektatuta zaudela eta saiatu konfiguratzen berriro"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Ziurtatu Internetera konektatuta zaudela eta saiatu berriro konfiguratzen"</string> <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satelite bidezko mezularitza ez dago erabilgarri"</string> <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satelite bidezko mezularitza ez dago erabilgarri herrialde edo lurralde honetan"</string> <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satelite bidezko mezularitza ez dago konfiguratuta"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 7627ff37d2bc..b87fe90df46c 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> تا <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"هر تقویمی"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> درحال قطع کردن بعضی از صداهاست"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"دستگاهتان یک مشکل داخلی دارد، و ممکن است تا زمانی که بازنشانی دادههای کارخانه انجام نگیرد، بیثبات بماند."</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 78fb2a07f756..867d32f55586 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kaikki kalenterit"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mykistää joitakin ääniä"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Laitteellasi on sisäinen ongelma, joka aiheuttaa epävakautta. Voit korjata tilanteen palauttamalla tehdasasetukset."</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 163d70d49230..3683248e73d7 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> à <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"N\'importe quel agenda"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à ses paramètres par défaut."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 75b8e3125da0..9f752db403c0 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> à <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Tous les agendas"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> coupe certains sons"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne lié à votre appareil est survenu. Ce dernier risque d\'être instable jusqu\'à ce que vous rétablissiez la configuration d\'usine."</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 70a7ec9c7a19..766fdbf2802f 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Calquera calendario"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando algúns sons"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Produciuse un erro interno no teu dispositivo e quizais funcione de maneira inestable ata o restablecemento dos datos de fábrica."</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 1535e4f5d463..ab32a7a3a06b 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>થી <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"કોઈપણ કૅલેન્ડર"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> અમુક અવાજોને મ્યૂટ કરે છે"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"તમારા ઉપકરણમાં આંતરિક સમસ્યા છે અને જ્યાં સુધી તમે ફેક્ટરી ડેટા ફરીથી સેટ કરશો નહીં ત્યાં સુધી તે અસ્થિર રહી શકે છે."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index c5825fe097db..3a07058e3bdd 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> से <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"किसी भी कैलेंडर के इवेंट के लिए"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> कुछ आवाज़ें म्यूट कर रहा है"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"आपके डिवाइस में कोई अंदरूनी समस्या है और यह तब तक ठीक नहीं होगी जब तक आप फ़ैक्टरी डेटा रीसेट नहीं करते."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 954f4cb1842c..010f09970a4e 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Na vašem uređaju postoji interni problem i možda neće biti stabilan dok ga ne vratite na tvorničko stanje."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 58a5cbdbc2ab..baace739145a 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bármilyen naptár"</string> <string name="muted_by" msgid="91464083490094950">"A(z) <xliff:g id="THIRD_PARTY">%1$s</xliff:g> lenémít néhány hangot"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Belső probléma van az eszközzel, és instabil lehet, amíg vissza nem állítja a gyári adatokat."</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index f457c42d9cd4..e8c6280c5f08 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Ցանկացած օրացույց"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ն անջատում է որոշ ձայներ"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Սարքում ներքին խնդիր է առաջացել և այն կարող է կրկնվել, մինչև չվերականգնեք գործարանային կարգավորումները:"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 88764c4112b8..0384ce5e9130 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> hingga <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kalender mana saja"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mematikan beberapa suara"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Ada masalah dengan perangkat. Hal ini mungkin membuat perangkat jadi tidak stabil dan perlu dikembalikan ke setelan pabrik."</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 8e92ed1532c1..b63ea1cbea7a 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> til <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Öll dagatöl"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> þaggar í einhverjum hljóðum"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Innra vandamál kom upp í tækinu og það kann að vera óstöðugt þangað til þú núllstillir það."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 49f1f21131e2..43cee8a03b51 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -169,7 +169,7 @@ <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Ok"</string> <string name="fcComplete" msgid="1080909484660507044">"Codice funzione completo."</string> <string name="fcError" msgid="5325116502080221346">"Problema di connessione o codice funzione non valido."</string> - <string name="httpErrorOk" msgid="6206751415788256357">"OK"</string> + <string name="httpErrorOk" msgid="6206751415788256357">"Ok"</string> <string name="httpError" msgid="3406003584150566720">"Si è verificato un errore di rete."</string> <string name="httpErrorLookup" msgid="3099834738227549349">"Impossibile trovare l\'URL."</string> <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Schema di autenticazione del sito non supportato."</string> @@ -1167,7 +1167,7 @@ <string name="VideoView_error_title" msgid="5750686717225068016">"Problemi video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Questo video non è valido per lo streaming su questo dispositivo."</string> <string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Impossibile riprodurre il video."</string> - <string name="VideoView_error_button" msgid="5138809446603764272">"OK"</string> + <string name="VideoView_error_button" msgid="5138809446603764272">"Ok"</string> <string name="relative_time" msgid="8572030016028033243">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="noon" msgid="8365974533050605886">"mezzogiorno"</string> <string name="Noon" msgid="6902418443846838189">"Mezzogiorno"</string> @@ -1206,7 +1206,7 @@ <string name="app_running_notification_text" msgid="5120815883400228566">"Tocca per ulteriori informazioni o per interrompere l\'app."</string> <string name="ok" msgid="2646370155170753815">"Ok"</string> <string name="cancel" msgid="6908697720451760115">"Annulla"</string> - <string name="yes" msgid="9069828999585032361">"OK"</string> + <string name="yes" msgid="9069828999585032361">"Ok"</string> <string name="no" msgid="5122037903299899715">"Annulla"</string> <string name="dialog_alert_title" msgid="651856561974090712">"Attenzione"</string> <string name="loading" msgid="3138021523725055037">"Caricamento…"</string> @@ -1265,7 +1265,7 @@ <string name="anr_activity_process" msgid="3477362583767128667">"L\'app <xliff:g id="ACTIVITY">%1$s</xliff:g> non risponde"</string> <string name="anr_application_process" msgid="4978772139461676184">"L\'app <xliff:g id="APPLICATION">%1$s</xliff:g> non risponde"</string> <string name="anr_process" msgid="1664277165911816067">"Il processo <xliff:g id="PROCESS">%1$s</xliff:g> non risponde"</string> - <string name="force_close" msgid="9035203496368973803">"OK"</string> + <string name="force_close" msgid="9035203496368973803">"Ok"</string> <string name="report" msgid="2149194372340349521">"Segnala"</string> <string name="wait" msgid="7765985809494033348">"Attendi"</string> <string name="webpage_unresponsive" msgid="7850879412195273433">"La pagina non risponde più.\n\nVuoi chiuderla?"</string> @@ -1395,7 +1395,7 @@ <string name="perms_description_app" msgid="2747752389870161996">"Fornito da <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="no_permissions" msgid="5729199278862516390">"Nessuna autorizzazione richiesta"</string> <string name="perm_costs_money" msgid="749054595022779685">"potrebbe comportare dei costi"</string> - <string name="dlg_ok" msgid="5103447663504839312">"OK"</string> + <string name="dlg_ok" msgid="5103447663504839312">"Ok"</string> <string name="usb_charging_notification_title" msgid="1674124518282666955">"Dispositivo in carica tramite USB"</string> <string name="usb_supplying_notification_title" msgid="5378546632408101811">"Dispositivo collegato in carica tramite USB"</string> <string name="usb_mtp_notification_title" msgid="1065989144124499810">"Trasferimento file tramite USB attivato"</string> @@ -1892,7 +1892,7 @@ <string name="restr_pin_try_later" msgid="5897719962541636727">"Riprova più tardi"</string> <string name="immersive_cling_title" msgid="2307034298721541791">"Visualizzazione a schermo intero"</string> <string name="immersive_cling_description" msgid="2896205051090870978">"Per uscire, scorri verso il basso dalla parte superiore dello schermo"</string> - <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string> + <string name="immersive_cling_positive" msgid="7047498036346489883">"Ok"</string> <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ruota per migliorare l\'anteprima"</string> <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Apri <xliff:g id="NAME">%s</xliff:g> a schermo intero per migliorare la visualizzazione"</string> <string name="done_label" msgid="7283767013231718521">"Fine"</string> @@ -1914,7 +1914,7 @@ <string name="package_installed_device_owner" msgid="8684974629306529138">"Installato dall\'amministratore.\nVai alle impostazioni per visualizzare le autorizzazioni concesse"</string> <string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string> - <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> + <string name="confirm_battery_saver" msgid="5247976246208245754">"Ok"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Il risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string> <string name="battery_saver_description" msgid="8518809702138617167">"Il risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string> <string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzionalità Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Per esempio, è possibile che le immagini non vengano visualizzate finché non le tocchi."</string> @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"Da <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualsiasi calendario"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> sta disattivando alcuni suoni"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Si è verificato un problema interno con il dispositivo, che potrebbe essere instabile fino al ripristino dei dati di fabbrica."</string> @@ -2150,7 +2152,7 @@ <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Questa notifica è stata posizionata più in basso. Tocca per dare un feedback."</string> <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notifiche avanzate"</string> <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Ora le risposte e le azioni suggerite vengono fornite dalle notifiche avanzate. Le notifiche adattive Android non sono più supportate."</string> - <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string> + <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"Ok"</string> <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Disattiva"</string> <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Scopri di più"</string> <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Le notifiche adattive Android sono state sostituite dalle notifiche avanzate in Android 12. Questa funzionalità mostra risposte e azioni suggerite e organizza le tue notifiche.\n\nLe notifiche avanzate possono accedere ai contenuti di una notifica, incluse le informazioni personali, come i nomi dei contatti e i messaggi. Questa funzionalità può anche ignorare le notifiche o rispondervi, ad esempio accettando le telefonate, e controllare la modalità Non disturbare."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 0e8f472cb107..0b70b9474310 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> עד <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"כל יומן"</string> <string name="muted_by" msgid="91464083490094950">"חלק מהצלילים מושתקים על ידי <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"קיימת בעיה פנימית במכשיר שלך, וייתכן שהוא לא יתפקד כראוי עד שיבוצע איפוס לנתוני היצרן."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 6eabd4afaf3d..b6a2b241d50c 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"すべてのカレンダー"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> により一部の音はミュートに設定"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"デバイスで内部的な問題が発生しました。データが初期化されるまで不安定になる可能性があります。"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 4ffa312f7c53..1554714570ec 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ნებისმიერი კალენდარი"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ზოგიერთ ხმას ადუმებს"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 6355d86a8ac9..f2a6b089ca6c 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Кез келген күнтізбе"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> кейбір дыбыстарды өшіруде"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string> @@ -2442,9 +2444,9 @@ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS функциясы қолжетімді емес"</string> <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS функциясы бұл елде немесе аймақта қолжетімді емес."</string> <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS функциясы реттелмеген"</string> - <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Жерсерік арқылы хабар алмасу үшін, Google Messages сіздің әдепкі хабар алмасу қолданбаңыз болуы керек."</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Жерсерік арқылы хабар алмасу үшін Google Messages сіздің әдепкі хабар алмасу қолданбаңыз болуы керек."</string> <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS функциясы қолжетімді емес"</string> - <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Satellite SOS функциясының бұл елде немесе аймақта қолжетімді екенін тексеру үшін, локация параметрлерін қосыңыз."</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Satellite SOS функциясының бұл елде немесе аймақта қолжетімді екенін тексеру үшін локация параметрлерін қосыңыз."</string> <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Жерсерік арқылы хабар алмасу функциясы қолжетімді"</string> <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Мобильдік немесе Wi-Fi желісі жоқ болған жағдайда, жерсерік арқылы хабар алмаса аласыз. Google Messages сіздің әдепкі хабар алмасу қолданбаңыз болуы керек."</string> <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Жерсерік арқылы хабар алмасу функциясына қолдау көрсетілмейді"</string> @@ -2454,9 +2456,9 @@ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Жерсерік арқылы хабар алмасу функциясы қолжетімді емес"</string> <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Жерсерік арқылы хабар алмасу функциясы бұл елде немесе аймақта қолжетімді емес."</string> <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Жерсерік арқылы хабар алмасу функциясы реттелмеген"</string> - <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Жерсерік арқылы хабар алмасу үшін, Google Messages сіздің әдепкі хабар алмасу қолданбаңыз болуы керек."</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Жерсерік арқылы хабар алмасу үшін Google Messages сіздің әдепкі хабар алмасу қолданбаңыз болуы керек."</string> <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Жерсерік арқылы хабар алмасу функциясы қолжетімді емес"</string> - <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Жерсерік арқылы хабар алмасу функциясының бұл елде немесе аймақта қолжетімді екенін тексеру үшін, локация параметрлерін қосыңыз."</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Жерсерік арқылы хабар алмасу функциясының бұл елде немесе аймақта қолжетімді екенін тексеру үшін локация параметрлерін қосыңыз."</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Саусақ ізімен ашу функциясын қайта реттеу"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> бұдан былай танылмайды."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> және <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> бұдан былай танылмайды."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 4163be41f7fd..287677ef7c5a 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ដល់ <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ប្រតិទិនណាមួយ"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> កំពុងបិទសំឡេងមួយចំនួន"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"មានបញ្ហាខាងក្នុងឧបករណ៍របស់អ្នក ហើយវាអ្នកមិនមានស្ថេរភាព រហូតទាល់តែអ្នកកំណត់ដូចដើមវិញទាំងស្រុង។"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 234ba66a5bc3..de44b8740376 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1387,7 +1387,7 @@ <string name="carrier_app_notification_title" msgid="5815477368072060250">"ಹೊಸ ಸಿಮ್ ಸೇರಿಸಲಾಗಿದೆ"</string> <string name="carrier_app_notification_text" msgid="6567057546341958637">"ಇದನ್ನು ಸ್ಥಾಪಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="time_picker_dialog_title" msgid="9053376764985220821">"ಸಮಯವನ್ನು ಹೊಂದಿಸಿ"</string> - <string name="date_picker_dialog_title" msgid="5030520449243071926">"ದಿನಾಂಕವನ್ನು ಹೊಂದಿಸಿ"</string> + <string name="date_picker_dialog_title" msgid="5030520449243071926">"ದಿನಾಂಕವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string> <string name="date_time_set" msgid="4603445265164486816">"ಹೊಂದಿಸು"</string> <string name="date_time_done" msgid="8363155889402873463">"ಆಯಿತು"</string> <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"ಹೊಸ: "</font></string> @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ನಿಂದ <xliff:g id="END">%2$s</xliff:g> ವರೆಗೆ"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ಯಾವುದೇ ಕ್ಯಾಲೆಂಡರ್"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಧ್ವನಿ ಮ್ಯೂಟ್ ಮಾಡುತ್ತಿದ್ದಾರೆ"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ ಹಾಗೂ ನೀವು ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾವನ್ನು ರೀಸೆಟ್ ಮಾಡುವವರೆಗೂ ಅದು ಅಸ್ಥಿರವಾಗಬಹುದು."</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 59af8fb584ce..7116c6e8c42e 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"모든 캘린더"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(이)가 일부 소리를 음소거함"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"사용 중인 기기 내부에 문제가 발생했습니다. 초기화할 때까지 불안정할 수 있습니다."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index cbb0a13963b3..63b5b3991a63 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Бардык жылнаамалар"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> айрым үндөрдү өчүрүүдө"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Түзмөгүңүздө ички көйгөй бар жана ал баштапкы абалга кайтарылмайынча туруктуу иштебей коюшу мүмкүн."</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 5af4a84c7caf..8a4da89d3a37 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ຫາ <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ປະຕິທິນໃດກໍໄດ້"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ປິດສຽງບາງຢ່າງໄວ້"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"ມີບັນຫາພາຍໃນກັບອຸປະກອນຂອງທ່ານ, ແລະມັນອາດຈະບໍ່ສະຖຽນຈົນກວ່າທ່ານຕັ້ງເປັນຂໍ້ມູນໂຮງງານຄືນແລ້ວ."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index cb7cc896d42f..e6cc84564301 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bet kuris kalendorius"</string> <string name="muted_by" msgid="91464083490094950">"„<xliff:g id="THIRD_PARTY">%1$s</xliff:g>“ nutildo kai kuriuos garsus"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Iškilo vidinė su jūsų įrenginiu susijusi problema, todėl įrenginys gali veikti nestabiliai, kol neatkursite gamyklinių duomenų."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index b72fb074ab14..515aaae297de 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"no <xliff:g id="START">%1$s</xliff:g> līdz <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Jebkurš kalendārs"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izslēdz noteiktas skaņas"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Jūsu ierīcē ir radusies iekšēja problēma, un ierīce var darboties nestabili. Lai to labotu, veiciet rūpnīcas datu atiestatīšanu."</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 0b0e3ef81cca..1a13752ae5a0 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> до <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Кој било календар"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> исклучи некои звуци"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Настана внатрешен проблем со уредот и може да биде нестабилен сè додека не ресетирате на фабричките податоци."</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index fe5974b3df24..3291e22c8c98 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> മുതൽ <xliff:g id="END">%2$s</xliff:g> വരെ"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"എല്ലാ കലണ്ടറിലും"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ചില ശബ്ദങ്ങൾ മ്യൂട്ട് ചെയ്യുന്നു"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു ആന്തരിക പ്രശ്നമുണ്ട്, ഫാക്ടറി വിവര പുനഃസജ്ജീകരണം ചെയ്യുന്നതുവരെ ഇതു അസ്ഥിരമായിരിക്കാനിടയുണ്ട്."</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 376a03350b23..f608c23b9a5c 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-с <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Дурын календарь"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> зарим дууны дууг хааж байна"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Таны төхөөрөмжид дотоод алдаа байна.Та төхөөрөмжөө үйлдвэрээс гарсан төлөвт шилжүүлэх хүртэл таны төхөөрөмж чинь тогтворгүй байж болох юм."</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index e640f729d093..b9c955ee5f49 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ते <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कोणतेही कॅलेंडर"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्वनी म्यूट करत आहे"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"आपल्या डिव्हाइसमध्ये अंतर्गत समस्या आहे आणि तुमचा फॅक्टरी डेटा रीसेट होईपर्यंत ती अस्थिर असू शकते."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 3946ac1df42f..01e070fa1f31 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> hingga <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Sebarang kalendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> meredamkan sesetengah bunyi"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Terdapat masalah dalaman dengan peranti anda. Peranti mungkin tidak stabil sehingga anda membuat tetapan semula data kilang."</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index fc0072557cb0..e957ea2977d8 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"၊ "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> မှ <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"မည်သည့်ပြက္ခဒိန်မဆို"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> သည် အချို့အသံကို ပိတ်နေသည်"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"သင့်ကိရိယာအတွင်းပိုင်းတွင် ပြဿနာရှိနေပြီး၊ မူလစက်ရုံထုတ်အခြေအနေအဖြစ် ပြန်လည်ရယူနိုင်သည်အထိ အခြေအနေမတည်ငြိမ်နိုင်ပါ။"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index c1095d3978ef..b2eaf959f01d 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> til <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Hvilken som helst kalender"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår av noen lyder"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Det har oppstått et internt problem på enheten din, og den kan være ustabil til du tilbakestiller den til fabrikkdata."</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index beda131e3de4..2eee141c099e 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1105,7 +1105,7 @@ <string name="permlab_addVoicemail" msgid="4770245808840814471">"भ्वाइसमेल थप गर्नुहोस्"</string> <string name="permdesc_addVoicemail" msgid="5470312139820074324">"तपाईँको भ्वाइसमेल इनबक्समा सन्देश थप्नको लागि एपलाई अनुमति दिन्छ।"</string> <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ले तपाईंको क्लिपबोर्डमा रहेको जानकारी पेस्ट गरेको छ"</string> - <string name="more_item_label" msgid="7419249600215749115">"बढी"</string> + <string name="more_item_label" msgid="7419249600215749115">"थप"</string> <string name="prepend_shortcut_label" msgid="1743716737502867951">"मेनु+"</string> <string name="menu_meta_shortcut_label" msgid="1623390163674762478">"Meta+"</string> <string name="menu_ctrl_shortcut_label" msgid="131911133027196485">"Ctrl+"</string> @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> देखि <xliff:g id="END">%2$s</xliff:g> सम्म"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कुनै पनि पात्रो"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही ध्वनिहरू म्युट गर्दै छ"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 48c8be196bc0..5a452239d1e6 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> tot <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Elke agenda"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index a99afa5a0a2f..afcb3d8f072f 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>ରୁ <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ଯେକୌଣସି କ୍ୟାଲେଣ୍ଡର୍"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> କିଛି ସାଉଣ୍ଡକୁ ମ୍ୟୁଟ୍ କରୁଛି"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"ଆପଣଙ୍କ ଡିଭାଇସ୍ରେ ଏକ ସମସ୍ୟା ରହିଛି ଏବଂ ଆପଣ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଏହା ଅସ୍ଥିର ରହିପାରେ।"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index b46301744701..8422b93f8aa1 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ਤੋਂ <xliff:g id="END">%2$s</xliff:g> ਤੱਕ"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ਕੋਈ ਵੀ ਕੈਲੰਡਰ"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ਕੁਝ ਧੁਨੀਆਂ ਨੂੰ ਮਿਊਟ ਕਰ ਰਹੀ ਹੈ"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਇੱਕ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆ ਹੈ ਅਤੇ ਇਹ ਅਸਥਿਰ ਹੋ ਸਕਦੀ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਨਹੀਂ ਕਰਦੇ।"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 4f34fc5ed84e..4eccff5d1688 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"Od <xliff:g id="START">%1$s</xliff:g> do <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Dowolny kalendarz"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> wycisza niektóre dźwięki"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"W Twoim urządzeniu wystąpił problem wewnętrzny. Może być ono niestabilne, dopóki nie przywrócisz danych fabrycznych."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index a2192767258a..173be307d973 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer agenda"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index a898121434e5..605e94e6181d 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer calendário"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está a desativar alguns sons."</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Existe um problema interno no seu dispositivo e pode ficar instável até efetuar uma reposição de dados de fábrica."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index a2192767258a..173be307d973 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer agenda"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 8b7a29683d8e..b709475a46cd 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Orice calendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"A apărut o problemă internă pe dispozitiv, iar acesta poate fi instabil până la revenirea la setările din fabrică."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 386830e83e66..b3f75cad48d4 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Любой календарь"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> приглушает некоторые звуки."</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Произошла внутренняя ошибка, и устройство может работать нестабильно, пока вы не выполните сброс настроек."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index e969a8ed523f..2dee88c73046 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="END">%2$s</xliff:g> සිට <xliff:g id="START">%1$s</xliff:g> දක්වා"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ඕනෑම දින දර්ශනයක්"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> සමහර ශබ්ද නිහඬ කරමින්"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"ඔබේ උපාංගය සමගින් ගැටලුවක් ඇති අතර, ඔබේ කර්මාන්තශාලා දත්ත යළි සකසන තෙක් එය අස්ථායි විය හැකිය."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index d211d8c481f7..177a55b9e24a 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Ľubovoľný kalendár"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypína niektoré zvuky"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Vo vašom zariadení došlo k internému problému. Môže byť nestabilné, kým neobnovíte jeho výrobné nastavenia."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index ca854340fc43..28ca6986867b 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> do <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kateri koli koledar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izklaplja nekatere zvoke"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Vaša naprava ima notranjo napako in bo morda nestabilna, dokler je ne ponastavite na tovarniške nastavitve."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 45cf31a9b0e8..7b4bc795b9bf 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Çdo kalendar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> po çaktivizon disa tinguj"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Ka një problem të brendshëm me pajisjen tënde. Ajo mund të jetë e paqëndrueshme derisa të rivendosësh të dhënat në gjendje fabrike."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 5a3bae0012bc..fe499c78832f 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1948,6 +1948,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Било који календар"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> искључује неке звуке"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Дошло је до интерног проблема у вези са уређајем и можда ће бити нестабилан док не обавите ресетовање на фабричка подешавања."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index fde90235e6ee..e037046d990b 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> till <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alla kalendrar"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> stänger av vissa ljud"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Ett internt problem har uppstått i enheten, och det kan hända att problemet kvarstår tills du återställer standardinställningarna."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 2a62c87d16d9..dde0637f843a 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> hadi <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kalenda yoyote"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> inazima baadhi ya sauti"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Kuna hitilafu ya ndani ya kifaa chako, na huenda kisiwe thabiti mpaka urejeshe mipangilio ya kiwandani."</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 6ff96c00ea8d..42cf38b0712b 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> முதல் <xliff:g id="END">%2$s</xliff:g> வரை"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ஏதேனும் கேலெண்டர்"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> சில ஒலிகளை முடக்குகிறது"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"சாதனத்தில் அகச் சிக்கல் இருக்கிறது, அதனை ஆரம்பநிலைக்கு மீட்டமைக்கும் வரை நிலையற்று இயங்கலாம்."</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 68fce1f8175a..c867d558f740 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> నుండి <xliff:g id="END">%2$s</xliff:g> వరకు"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ఏదైనా క్యాలెండర్"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string> @@ -2442,7 +2444,7 @@ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"ఎమర్జెన్సీ శాటిలైట్ సహాయం అందుబాటులో లేదు"</string> <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"ఈ దేశంలో లేదా ప్రాంతంలో ఎమర్జెన్సీ శాటిలైట్ సహాయం అందుబాటులో లేదు"</string> <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ఎమర్జెన్సీ శాటిలైట్ సహాయం సెటప్ చేయలేదు"</string> - <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"శాటిలైట్ ద్వారా మెసేజ్ చేయడానికి, Google Messagesను మీ ఆటోమేటిక్ సెట్టింగ్ మెసేజింగ్ యాప్గా సెట్ చేయండి"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"శాటిలైట్ ద్వారా మెసేజ్ చేయడానికి, Google Messagesను మీ ఆటోమేటిక్ మెసేజింగ్ యాప్గా సెట్ చేయండి"</string> <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"ఎమర్జెన్సీ శాటిలైట్ సహాయం అందుబాటులో లేదు"</string> <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ఈ దేశంలో లేదా ప్రాంతంలో ఎమర్జెన్సీ శాటిలైట్ సహాయం అందుబాటులో ఉందో లేదో చెక్ చేయడానికి, లొకేషన్ సెట్టింగ్లను ఆన్ చేయండి"</string> <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"శాటిలైట్ మెసేజింగ్ అందుబాటులో ఉంది"</string> @@ -2454,7 +2456,7 @@ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"శాటిలైట్ మెసేజింగ్ అందుబాటులో లేదు"</string> <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"ఈ దేశంలో లేదా ప్రాంతంలో శాటిలైట్ మెసేజింగ్ అందుబాటులో లేదు"</string> <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"శాటిలైట్ మెసేజింగ్ను సెటప్ చేయలేదు"</string> - <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"శాటిలైట్ ద్వారా మెసేజ్ చేయడానికి, Google Messagesను మీ ఆటోమేటిక్ సెట్టింగ్ మెసేజింగ్ యాప్గా సెట్ చేయండి"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"శాటిలైట్ ద్వారా మెసేజ్ చేయడానికి, Google Messagesను మీ ఆటోమేటిక్ మెసేజింగ్ యాప్గా సెట్ చేయండి"</string> <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"శాటిలైట్ మెసేజింగ్ అందుబాటులో లేదు"</string> <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ఈ దేశంలో లేదా ప్రాంతంలో శాటిలైట్ మెసేజింగ్ అందుబాటులో ఉందో లేదో చెక్ చేయడానికి, లొకేషన్ సెట్టింగ్లను ఆన్ చేయండి"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"వేలిముద్ర అన్లాక్ను మళ్లీ సెటప్ చేయండి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 178a76d3a404..7af9fb9c8d26 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>ถึง<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ปฏิทินทั้งหมด"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> กำลังปิดเสียงบางรายการ"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"อุปกรณ์ของคุณเกิดปัญหาภายในเครื่อง อุปกรณ์อาจทำงานไม่เสถียรจนกว่าคุณจะรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 8e3c5e7433a7..5de981ca825b 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> patungong <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Anumang kalendaryo"</string> <string name="muted_by" msgid="91464083490094950">"Minu-mute ng <xliff:g id="THIRD_PARTY">%1$s</xliff:g> ang ilang tunog"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"May internal na problema sa iyong device, at maaaring hindi ito maging stable hanggang sa i-reset mo ang factory data."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 98b1dd3ef245..28eb125382e6 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Tüm takvimler"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bazı sesleri kapatıyor"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızla ilgili dahili bir sorun oluştu ve fabrika verilerine sıfırlama işlemi gerçekleştirilene kadar kararsız çalışabilir."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index d5e6bd81ffcc..ae2cab56a96b 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1949,6 +1949,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"З усіх календарів"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає деякі звуки"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Через внутрішню помилку ваш пристрій може працювати нестабільно. Відновіть заводські налаштування."</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index b26b1c102abc..6a96bc21be70 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> تا <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"کوئی بھی کیلنڈر"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کچھ آوازوں کو خاموش کر رہا ہے"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"آپ کے آلہ میں ایک داخلی مسئلہ ہے اور جب تک آپ فیکٹری ڈیٹا کو دوبارہ ترتیب نہیں دے دیتے ہیں، ہوسکتا ہے کہ یہ غیر مستحکم رہے۔"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 97e044887d7e..42b236aae2e8 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Har qanday taqvim"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ayrim tovushlarni ovozsiz qilgan"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Qurilmangiz bilan bog‘liq ichki muammo mavjud. U zavod sozlamalari tiklanmaguncha barqaror ishlamasligi mumkin."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 1ebff32f7d20..ae3976333295 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> đến <xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bất kỳ lịch nào"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> đang tắt một số âm thanh"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Đã xảy ra sự cố nội bộ với thiết bị của bạn và thiết bị có thể sẽ không ổn định cho tới khi bạn thiết lập lại dữ liệu ban đầu."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 64c2f70be73e..fbab421da8f2 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>到<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"所有日历"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正在将某些音效设为静音"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"您的设备内部出现了问题。如果不将设备恢复出厂设置,设备运行可能会不稳定。"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 53c5d155cd27..43da71ecf3a0 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>至<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正將某些音效設為靜音"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"你裝置的系統發生問題,回復原廠設定後即可解決該問題。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index cbd6bd2fb48f..628fda71c9d5 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>到<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string> <string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>」正在關閉部分音效"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"你的裝置發生內部問題,必須將裝置恢復原廠設定才能解除不穩定狀態。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index f498fe24267c..878275a8064a 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1947,6 +1947,8 @@ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string> <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string> <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"U-<xliff:g id="START">%1$s</xliff:g> ukuya ku-<xliff:g id="END">%2$s</xliff:g>"</string> + <!-- no translation found for zen_mode_trigger_summary_combined (6492381546327807669) --> + <skip /> <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Noma iyiphi ikhalenda"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ithulisa eminye imisindo"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Kukhona inkinga yangaphakathi ngedivayisi yakho, futhi ingase ibe engazinzile kuze kube yilapho usetha kabusha yonke idatha."</string> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 7ef539492aa4..08cb4de03536 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1867,8 +1867,12 @@ 16 KB device. 4 KB natives libs will be loaded app-compat mode if they are eligible. @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) --> <attr name="pageSizeCompat"> - <enum name="enabled" value="5" /> - <enum name="disabled" value="6" /> + <!-- value for enabled must match with + ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED --> + <enum name="enabled" value="32" /> + <!-- value for disabled must match with + ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED --> + <enum name="disabled" value="64" /> </attr> @@ -2568,6 +2572,8 @@ against a development branch, in which case it will only work against the development builds. --> <attr name="minSdkVersion" format="integer|string" /> + <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SUPPORT_MINOR_VERSIONS_IN_MINSDKVERSION) --> + <attr name="minSdkVersionFull" format="string" /> <!-- This is the SDK version number that the application is targeting. It is able to run on older versions (down to minSdkVersion), but was explicitly tested to work with the version specified here. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 38ebda733270..0d13ca83ec59 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2779,6 +2779,9 @@ If empty, logs "other" for all. --> <string-array name="config_loggable_dream_prefixes"></string-array> + <!-- Whether to enable glanceable hub features on this device. --> + <bool name="config_glanceableHubEnabled">false</bool> + <!-- ComponentName of a dream to show whenever the system would otherwise have gone to sleep. When the PowerManager is asked to go to sleep, it will instead try to start this dream if possible. The dream should typically call startDozing() @@ -6003,6 +6006,12 @@ <!-- If true, show multiuser switcher by default unless the user specifically disables it. --> <bool name="config_showUserSwitcherByDefault">false</bool> + <!-- If true, user can change state of multiuser switcher. --> + <bool name="config_allowChangeUserSwitcherEnabled">true</bool> + + <!-- If true, multiuser switcher would be automatically enabled when second user is created on the device. --> + <bool name="config_enableUserSwitcherUponUserCreation">true</bool> + <!-- Set to true to make assistant show in front of the dream/screensaver. --> <bool name="config_assistantOnTopOfDream">false</bool> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index e75371d6bf46..76ff56559adf 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -135,6 +135,8 @@ <public name="pageSizeCompat" /> <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) --> <public name="shareRolePriority"/> + <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SUPPORT_MINOR_VERSIONS_IN_MINSDKVERSION) --> + <public name="minSdkVersionFull"/> </staging-public-group> <staging-public-group type="id" first-id="0x01b60000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 413f0c3e0c58..d498b9191559 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -199,6 +199,21 @@ <!-- Displayed to confirm to the user that caller ID will not be restricted on the next call or in general. --> <string name="CLIRDefaultOffNextCallOff">Caller ID defaults to not restricted. Next call: Not restricted</string> + <!-- Message displayed in dialog when APK is not 16 KB aligned. [CHAR LIMIT=NONE] --> + <string name="page_size_compat_apk_warning">This app isn’t 16 KB compatible. APK alignment check failed. + This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. + For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> </string> + + <!-- Message displayed in dialog when ELF is not 16 KB aligned. [CHAR LIMIT=NONE] --> + <string name="page_size_compat_elf_warning">This app isn’t 16 KB compatible. ELF alignment check failed. + This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. + For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a></string> + + <!-- Message displayed in dialog when APK and ELF are not 16 KB aligned. [CHAR LIMIT=NONE] --> + <string name="page_size_compat_apk_and_elf_warning">This app isn’t 16 KB compatible. APK and ELF alignment checks failed. + This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. + For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a></string> + <!-- Displayed to tell the user that caller ID is not provisioned for their SIM. --> <string name="serviceNotProvisioned">Service not provisioned.</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fc24f4599b25..4f029cdcacc9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2054,6 +2054,7 @@ <java-symbol type="bool" name="config_allowTheaterModeWakeFromDock" /> <java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" /> <java-symbol type="bool" name="config_keepDreamingWhenUnplugging" /> + <java-symbol type="bool" name="config_glanceableHubEnabled" /> <java-symbol type="integer" name="config_keyguardDrawnTimeout" /> <java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" /> <java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" /> @@ -2393,6 +2394,8 @@ <java-symbol type="layout" name="notification_2025_template_expanded_base" /> <java-symbol type="layout" name="notification_2025_template_heads_up_base" /> <java-symbol type="layout" name="notification_2025_template_header" /> + <java-symbol type="layout" name="notification_2025_template_collapsed_messaging" /> + <java-symbol type="layout" name="notification_2025_template_collapsed_media" /> <java-symbol type="layout" name="notification_template_material_base" /> <java-symbol type="layout" name="notification_template_material_heads_up_base" /> <java-symbol type="layout" name="notification_template_material_compact_heads_up_base" /> @@ -3310,6 +3313,11 @@ <java-symbol type="string" name="language_selection_title" /> <java-symbol type="string" name="search_language_hint" /> + <!-- Strings for page size app compat dialog --> + <java-symbol type="string" name="page_size_compat_apk_warning" /> + <java-symbol type="string" name="page_size_compat_elf_warning" /> + <java-symbol type="string" name="page_size_compat_apk_and_elf_warning" /> + <!-- Work profile unlaunchable app alert dialog--> <java-symbol type="style" name="AlertDialogWithEmergencyButton"/> <java-symbol type="string" name="work_mode_emergency_call_button" /> @@ -3441,6 +3449,8 @@ <!-- Notifications: CallStyle --> <java-symbol type="layout" name="notification_template_material_call" /> <java-symbol type="layout" name="notification_template_material_big_call" /> + <java-symbol type="layout" name="notification_2025_template_collapsed_call" /> + <java-symbol type="layout" name="notification_2025_template_expanded_call" /> <java-symbol type="string" name="call_notification_answer_action" /> <java-symbol type="string" name="call_notification_answer_video_action" /> <java-symbol type="string" name="call_notification_decline_action" /> @@ -4092,6 +4102,7 @@ <java-symbol type="layout" name="notification_template_messaging_text_message" /> <java-symbol type="layout" name="notification_template_messaging_image_message" /> <java-symbol type="layout" name="notification_template_messaging_group" /> + <java-symbol type="layout" name="notification_2025_messaging_group" /> <java-symbol type="id" name="message_text" /> <java-symbol type="id" name="message_name" /> <java-symbol type="id" name="message_icon" /> @@ -4622,9 +4633,11 @@ <java-symbol type="dimen" name="conversation_icon_container_top_padding" /> <java-symbol type="dimen" name="conversation_icon_container_top_padding_small_avatar" /> <java-symbol type="layout" name="notification_template_material_conversation" /> + <java-symbol type="layout" name="notification_2025_template_conversation" /> <java-symbol type="dimen" name="button_padding_horizontal_material" /> <java-symbol type="dimen" name="button_inset_horizontal_material" /> <java-symbol type="layout" name="conversation_face_pile_layout" /> + <java-symbol type="layout" name="notification_2025_conversation_face_pile_layout" /> <java-symbol type="string" name="unread_convo_overflow" /> <java-symbol type="drawable" name="conversation_badge_background" /> <java-symbol type="drawable" name="conversation_badge_ring" /> @@ -4695,6 +4708,8 @@ <!-- If true, show multiuser switcher by default unless the user specifically disables it. --> <java-symbol type="bool" name="config_showUserSwitcherByDefault" /> + <java-symbol type="bool" name="config_allowChangeUserSwitcherEnabled" /> + <java-symbol type="bool" name="config_enableUserSwitcherUponUserCreation" /> <!-- Set to true to make assistant show in front of the dream/screensaver. --> <java-symbol type="bool" name="config_assistantOnTopOfDream"/> diff --git a/core/res/res/xml/bookmarks.xml b/core/res/res/xml/bookmarks.xml index e735784ee5bb..17860ef6d9f2 100644 --- a/core/res/res/xml/bookmarks.xml +++ b/core/res/res/xml/bookmarks.xml @@ -20,14 +20,10 @@ Typical shortcuts (not necessarily defined here): 'b': Browser - 'c': Contacts + 'p': Contacts 'e': Email - 'g': GMail - 'k': Calendar + 'c': Calendar 'm': Maps - 'p': Music - 's': SMS - 't': Talk 'u': Calculator 'y': YouTube --> @@ -38,7 +34,7 @@ androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_CONTACTS" - androidprv:keycode="KEYCODE_C" + androidprv:keycode="KEYCODE_P" androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_EMAIL" @@ -46,21 +42,13 @@ androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_CALENDAR" - androidprv:keycode="KEYCODE_K" + androidprv:keycode="KEYCODE_C" androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_MAPS" androidprv:keycode="KEYCODE_M" androidprv:modifierState="META" /> <bookmark - category="android.intent.category.APP_MUSIC" - androidprv:keycode="KEYCODE_P" - androidprv:modifierState="META" /> - <bookmark - role="android.app.role.SMS" - androidprv:keycode="KEYCODE_S" - androidprv:modifierState="META" /> - <bookmark category="android.intent.category.APP_CALCULATOR" androidprv:keycode="KEYCODE_U" androidprv:modifierState="META" /> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png Binary files differnew file mode 100644 index 000000000000..c7e937c6d77a --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png Binary files differnew file mode 100644 index 000000000000..a3cff989bad8 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml new file mode 100644 index 000000000000..c9fb53ee78da --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M720,600L720,520L800,520Q817,520 828.5,531.5Q840,543 840,560Q840,577 828.5,588.5Q817,600 800,600L720,600ZM720,760L720,680L800,680Q817,680 828.5,691.5Q840,703 840,720Q840,737 828.5,748.5Q817,760 800,760L720,760ZM560,800Q527,800 503.5,776.5Q480,753 480,720L400,720L400,560L480,560Q480,527 503.5,503.5Q527,480 560,480L680,480L680,800L560,800ZM280,680Q214,680 167,633Q120,586 120,520Q120,454 167,407Q214,360 280,360L340,360Q365,360 382.5,342.5Q400,325 400,300Q400,275 382.5,257.5Q365,240 340,240L200,240Q183,240 171.5,228.5Q160,217 160,200Q160,183 171.5,171.5Q183,160 200,160L340,160Q398,160 439,201Q480,242 480,300Q480,358 439,399Q398,440 340,440L280,440Q247,440 223.5,463.5Q200,487 200,520Q200,553 223.5,576.5Q247,600 280,600L360,600L360,680L280,680Z" /> +</vector>
\ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml new file mode 100644 index 000000000000..ca9482503c92 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z" /> +</vector>
\ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml new file mode 100644 index 000000000000..48f990c3ba37 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,720L680,240L280,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z" /> +</vector>
\ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml index be0e135af23a..e1f46232488c 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,46 +13,127 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="8dp" - android:paddingBottom="8dp"> - - <ImageView - android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_marginEnd="8dp" - android:paddingBottom="8dp"/> - - <TextView - android:id="@+id/title" - android:layout_width="0dp" - android:layout_weight="1" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearanceBody"/> + android:orientation="vertical"> - <TextView - android:id="@+id/value1" - android:layout_width="wrap_content" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:gravity="right" - android:maxLines="1" - android:textAppearance="@style/TextAppearanceBody"/> - - <TextView - android:id="@+id/value2" - android:layout_width="76dp" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:orientation="horizontal" + android:paddingBottom="8dp" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingTop="8dp"> + + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginEnd="8dp" + android:paddingBottom="8dp" /> + + <TextView + android:id="@+id/title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="@style/TextAppearanceBody" /> + + <TextView + android:id="@+id/value1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:gravity="right" + android:maxLines="1" + android:textAppearance="@style/TextAppearanceBody" /> + + <TextView + android:id="@+id/value2" + android:layout_width="76dp" + android:layout_height="wrap_content" + android:gravity="right" + android:maxLines="1" + android:textAppearance="@style/TextAppearanceBody" /> + </LinearLayout> + + <TableLayout + android:id="@+id/table" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="right" - android:maxLines="1" - android:textAppearance="@style/TextAppearanceBody"/> + android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" + android:layout_marginStart="50dp" + android:stretchColumns="1,2,3,4"> + + <TableRow android:background="#EEFFEE"> + <LinearLayout + style="@style/TableCell.Start" + android:layout_width="65dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="3dip" + android:text="State" + android:textStyle="bold" /> + </LinearLayout> + + <RelativeLayout style="@style/TableCell.Inner"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_on_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + + <RelativeLayout style="@style/TableCell.Inner"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_off_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + + <RelativeLayout style="@style/TableCell.Inner"> + <ImageView + android:id="@+id/screen_on_24_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_on_24" + android:tint="@color/battery_consumer_slice_icon" /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/screen_on_24_icon" + android:src="@drawable/power_other_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + + <RelativeLayout style="@style/TableCell.End"> + <ImageView + android:id="@+id/screen_off_24_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_off_24" + android:tint="@color/battery_consumer_slice_icon" /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/screen_off_24_icon" + android:src="@drawable/power_other_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + </TableRow> + + <View + android:layout_height="1dip" + android:background="#000000" /> + </TableLayout> </LinearLayout> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml index 987de6bcb4f4..b88425a57187 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml @@ -14,16 +14,30 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<androidx.swiperefreshlayout.widget.SwipeRefreshLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/swipe_refresh" - android:paddingTop="?attr/actionBarSize" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:orientation="vertical" + android:fitsSystemWindows="true"> - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/list_view" + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + android:elevation="4dp" + android:theme="@style/ThemeOverlay.AppCompat.ActionBar" /> -</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> + <androidx.swiperefreshlayout.widget.SwipeRefreshLayout + android:id="@+id/swipe_refresh" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/list_view" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> +</LinearLayout> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml new file mode 100644 index 000000000000..642c0deac6f4 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<TableRow xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <LinearLayout style="@style/TableCell.Start"> + <TextView + android:id="@+id/procState" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.Inner" + android:orientation="vertical"> + <TextView + android:id="@+id/power_b_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_b_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.Inner" + android:orientation="vertical"> + <TextView + android:id="@+id/power_b_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_b_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.Inner" + android:orientation="vertical"> + <TextView + android:id="@+id/power_c_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_c_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.End" + android:orientation="vertical"> + <TextView + android:id="@+id/power_c_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_c_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> +</TableRow> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml index 2d276a51a1da..46d8f04f9eef 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml @@ -17,13 +17,12 @@ <androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swipe_refresh" - android:paddingTop="?attr/actionBarSize" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:fitsSystemWindows="true"> <LinearLayout android:orientation="vertical" - android:paddingTop="?attr/actionBarSize" android:layout_width="match_parent" android:layout_height="match_parent"> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml index 6cc70bd1af61..1dc288af89b6 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml @@ -18,4 +18,5 @@ <resources> <color name="battery_consumer_bg_power_profile">#ffffff</color> <color name="battery_consumer_bg_energy_consumption">#fff5eb</color> + <color name="battery_consumer_slice_icon">#aaaaaa</color> </resources> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml index fa30b2c8dc6f..a298cc9f59e3 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml @@ -17,11 +17,9 @@ --> <resources> - <style name="Theme" parent="Theme.MaterialComponents.Light"> + <style name="Theme" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="colorPrimary">#34a853</item> - <item name="android:windowActionBar">true</item> - <item name="android:windowNoTitle">false</item> - <item name="android:windowDrawsSystemBarBackgrounds">false</item> + <item name="toolbarStyle">@style/Widget.AppCompat.Toolbar</item> </style> <style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView"> @@ -32,4 +30,25 @@ <item name="android:textColor">#000000</item> <item name="android:textSize">18sp</item> </style> -</resources>
\ No newline at end of file + + <style name="TableCell"> + <item name="android:layout_height">match_parent</item> + <item name="android:padding">4dp</item> + </style> + + <style name="TableCell.Start" parent="TableCell"> + <item name="android:background">@drawable/border_ltr</item> + </style> + + <style name="TableCell.Inner" parent="TableCell"> + <item name="android:background">@drawable/border_tr</item> + <item name="android:layout_width">0dp</item> + <item name="android:layout_weight">1</item> + </style> + + <style name="TableCell.End" parent="TableCell"> + <item name="android:background">@drawable/border_tr</item> + <item name="android:layout_width">0dp</item> + <item name="android:layout_weight">1</item> + </style> +</resources> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java index f691a1b90934..35175a755e89 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java @@ -31,22 +31,16 @@ public class BatteryConsumerData { public static final String UID_BATTERY_CONSUMER_ID_PREFIX = "APP|"; public static final String AGGREGATE_BATTERY_CONSUMER_ID = "SYS|"; - enum EntryType { - UID_TOTAL_POWER, - UID_POWER_PROFILE, - UID_POWER_PROFILE_PROCESS_STATE, - UID_POWER_ENERGY_CONSUMPTION, - UID_POWER_ENERGY_PROCESS_STATE, - UID_POWER_CUSTOM, - UID_DURATION, + public enum EntryType { DEVICE_TOTAL_POWER, - DEVICE_POWER_MODELED, + DEVICE_POWER, DEVICE_POWER_ENERGY_CONSUMPTION, DEVICE_POWER_CUSTOM, DEVICE_DURATION, + UID, } - enum ConsumerType { + public enum ConsumerType { UID_BATTERY_CONSUMER, DEVICE_POWER_COMPONENT, } @@ -56,34 +50,38 @@ public class BatteryConsumerData { public String title; public double value1; public double value2; + public List<Slice> slices; + } + + public static class Slice { + public int powerState; + public int screenState; + public int processState; + public double powerMah; + public long durationMs; } private BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo; private final List<Entry> mEntries = new ArrayList<>(); public BatteryConsumerData(Context context, - List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) { + BatteryUsageStats batteryUsageStats, String batteryConsumerId) { switch (getConsumerType(batteryConsumerId)) { case UID_BATTERY_CONSUMER: - populateForUidBatteryConsumer(context, batteryUsageStatsList, batteryConsumerId); + populateForUidBatteryConsumer(context, batteryUsageStats, batteryConsumerId); break; case DEVICE_POWER_COMPONENT: - populateForAggregateBatteryConsumer(context, batteryUsageStatsList); + populateForAggregateBatteryConsumer(context, batteryUsageStats); break; } } - private void populateForUidBatteryConsumer( - Context context, List<BatteryUsageStats> batteryUsageStatsList, + private void populateForUidBatteryConsumer(Context context, BatteryUsageStats batteryUsageStats, String batteryConsumerId) { - BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0); - BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1); BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats, batteryConsumerId); - BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer( - modeledBatteryUsageStats, batteryConsumerId); - if (requestedBatteryConsumer == null || requestedModeledBatteryConsumer == null) { + if (requestedBatteryConsumer == null) { mBatteryConsumerInfo = null; return; } @@ -92,118 +90,95 @@ public class BatteryConsumerData { batteryUsageStats, batteryConsumerId, context.getPackageManager()); double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT]; - double[] totalModeledPowerByComponentMah = - new double[BatteryConsumer.POWER_COMPONENT_COUNT]; long[] totalDurationByComponentMs = new long[BatteryConsumer.POWER_COMPONENT_COUNT]; - final int customComponentCount = - requestedBatteryConsumer.getCustomPowerComponentCount(); + final int customComponentCount = requestedBatteryConsumer.getCustomPowerComponentCount(); double[] totalCustomPowerByComponentMah = new double[customComponentCount]; computeTotalPower(batteryUsageStats, totalPowerByComponentMah); - computeTotalPower(modeledBatteryUsageStats, totalModeledPowerByComponentMah); computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah); computeTotalDuration(batteryUsageStats, totalDurationByComponentMs); - if (isPowerProfileModelsOnly(requestedBatteryConsumer)) { - addEntry("Consumed", EntryType.UID_TOTAL_POWER, - requestedBatteryConsumer.getConsumedPower(), - batteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .getConsumedPower()); - } else { - addEntry("Consumed (PowerStats)", EntryType.UID_TOTAL_POWER, - requestedBatteryConsumer.getConsumedPower(), - batteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .getConsumedPower()); - addEntry("Consumed (PowerProfile)", EntryType.UID_TOTAL_POWER, - requestedModeledBatteryConsumer.getConsumedPower(), - modeledBatteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .getConsumedPower()); + Entry totalsEntry = addEntry("Consumed", EntryType.UID, + requestedBatteryConsumer.getConsumedPower(), + batteryUsageStats.getAggregateBatteryConsumer( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) + .getConsumedPower()); + addSlices(totalsEntry, requestedBatteryConsumer, BatteryConsumer.POWER_COMPONENT_BASE); + for (Slice slice : totalsEntry.slices) { + slice.powerMah = requestedBatteryConsumer.getConsumedPower( + new BatteryConsumer.Dimensions(BatteryConsumer.POWER_COMPONENT_ANY, + slice.processState, slice.screenState, slice.powerState)); } for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { + if (component == BatteryConsumer.POWER_COMPONENT_BASE) { + continue; + } final String metricTitle = getPowerMetricTitle(component); - final int powerModel = requestedBatteryConsumer.getPowerModel(component); - if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE - || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) { - addEntry(metricTitle, EntryType.UID_POWER_PROFILE, - requestedBatteryConsumer.getConsumedPower(component), + double consumedPower = requestedBatteryConsumer.getConsumedPower(component); + if (consumedPower != 0) { + Entry entry = addEntry(metricTitle, EntryType.UID, consumedPower, totalPowerByComponentMah[component]); - addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE, - requestedBatteryConsumer, component); - } else { - addEntry(metricTitle + " (PowerStats)", EntryType.UID_POWER_ENERGY_CONSUMPTION, - requestedBatteryConsumer.getConsumedPower(component), - totalPowerByComponentMah[component]); - addProcessStateEntries(metricTitle, EntryType.UID_POWER_ENERGY_PROCESS_STATE, - requestedBatteryConsumer, component); - addEntry(metricTitle + " (PowerProfile)", EntryType.UID_POWER_PROFILE, - requestedModeledBatteryConsumer.getConsumedPower(component), - totalModeledPowerByComponentMah[component]); - addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE, - requestedModeledBatteryConsumer, component); + addSlices(entry, requestedBatteryConsumer, component); } } for (int component = 0; component < customComponentCount; component++) { - final String name = requestedBatteryConsumer.getCustomPowerComponentName( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component); - addEntry(name + " (PowerStats)", EntryType.UID_POWER_CUSTOM, - requestedBatteryConsumer.getConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component), - totalCustomPowerByComponentMah[component] - ); - } - - for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { - final String metricTitle = getTimeMetricTitle(component); - addEntry(metricTitle, EntryType.UID_DURATION, - requestedBatteryConsumer.getUsageDurationMillis(component), - totalDurationByComponentMs[component] - ); + int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component; + final String name = requestedBatteryConsumer.getCustomPowerComponentName(componentId); + double consumedPower = requestedBatteryConsumer.getConsumedPower(componentId); + if (consumedPower != 0) { + Entry entry = addEntry(name, EntryType.UID, consumedPower, + totalCustomPowerByComponentMah[component]); + addSlices(entry, requestedBatteryConsumer, componentId); + } } mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats, batteryConsumerId, context.getPackageManager()); } - private void addProcessStateEntries(String metricTitle, EntryType entryType, - BatteryConsumer batteryConsumer, int component) { + private void addSlices(Entry entry, BatteryConsumer batteryConsumer, int component) { final BatteryConsumer.Key[] keys = batteryConsumer.getKeys(component); if (keys == null || keys.length <= 1) { return; } + boolean hasProcStateData = false; for (BatteryConsumer.Key key : keys) { - String label; - switch (key.processState) { - case BatteryConsumer.PROCESS_STATE_FOREGROUND: - label = "foreground"; - break; - case BatteryConsumer.PROCESS_STATE_BACKGROUND: - label = "background"; - break; - case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE: - label = "FGS"; - break; - case BatteryConsumer.PROCESS_STATE_CACHED: - label = "cached"; - break; - default: - continue; + if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { + hasProcStateData = true; + break; } - addEntry(metricTitle + " \u2022 " + label, entryType, - batteryConsumer.getConsumedPower(key), 0); } + + ArrayList<Slice> slices = new ArrayList<>(); + for (BatteryConsumer.Key key : keys) { + if (hasProcStateData && key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { + continue; + } + + double powerMah = batteryConsumer.getConsumedPower(key); + long durationMs = batteryConsumer.getUsageDurationMillis(key); + + if (powerMah == 0 && durationMs == 0) { + continue; + } + + Slice slice = new Slice(); + slice.powerState = key.powerState; + slice.screenState = key.screenState; + slice.processState = key.processState; + slice.powerMah = powerMah; + slice.durationMs = durationMs; + + slices.add(slice); + } + entry.slices = slices; } private void populateForAggregateBatteryConsumer(Context context, - List<BatteryUsageStats> batteryUsageStatsList) { - BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0); - BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1); - + BatteryUsageStats batteryUsageStats) { final BatteryConsumer deviceBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); @@ -211,46 +186,18 @@ public class BatteryConsumerData { batteryUsageStats.getAggregateBatteryConsumer( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); - BatteryConsumer modeledDeviceBatteryConsumer = - modeledBatteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); - BatteryConsumer modeledAppsBatteryConsumer = - modeledBatteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); - - if (isPowerProfileModelsOnly(deviceBatteryConsumer)) { - addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER, - deviceBatteryConsumer.getConsumedPower(), - appsBatteryConsumer.getConsumedPower()); - } else { - addEntry("Consumed (PowerStats)", EntryType.DEVICE_TOTAL_POWER, - deviceBatteryConsumer.getConsumedPower(), - appsBatteryConsumer.getConsumedPower()); - addEntry("Consumed (PowerProfile)", EntryType.DEVICE_TOTAL_POWER, - modeledDeviceBatteryConsumer.getConsumedPower(), - modeledAppsBatteryConsumer.getConsumedPower()); - } + addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER, + deviceBatteryConsumer.getConsumedPower(), + appsBatteryConsumer.getConsumedPower()); mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats, AGGREGATE_BATTERY_CONSUMER_ID, context.getPackageManager()); - for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { final String metricTitle = getPowerMetricTitle(component); - final int powerModel = deviceBatteryConsumer.getPowerModel(component); - if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE - || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) { - addEntry(metricTitle, EntryType.DEVICE_POWER_MODELED, - deviceBatteryConsumer.getConsumedPower(component), - appsBatteryConsumer.getConsumedPower(component)); - } else { - addEntry(metricTitle + " (PowerStats)", EntryType.DEVICE_POWER_ENERGY_CONSUMPTION, - deviceBatteryConsumer.getConsumedPower(component), - appsBatteryConsumer.getConsumedPower(component)); - addEntry(metricTitle + " (PowerProfile)", EntryType.DEVICE_POWER_MODELED, - modeledDeviceBatteryConsumer.getConsumedPower(component), - modeledAppsBatteryConsumer.getConsumedPower(component)); - } + addEntry(metricTitle, EntryType.DEVICE_POWER, + deviceBatteryConsumer.getConsumedPower(component), + appsBatteryConsumer.getConsumedPower(component)); } final int customComponentCount = @@ -258,10 +205,10 @@ public class BatteryConsumerData { for (int component = 0; component < customComponentCount; component++) { final String name = deviceBatteryConsumer.getCustomPowerComponentName( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component); - addEntry(name + " (PowerStats)", EntryType.DEVICE_POWER_CUSTOM, - deviceBatteryConsumer.getConsumedPowerForCustomComponent( + addEntry(name, EntryType.DEVICE_POWER_CUSTOM, + deviceBatteryConsumer.getConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component), - appsBatteryConsumer.getConsumedPowerForCustomComponent( + appsBatteryConsumer.getConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component)); } @@ -272,17 +219,6 @@ public class BatteryConsumerData { } } - private boolean isPowerProfileModelsOnly(BatteryConsumer batteryConsumer) { - for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { - final int powerModel = batteryConsumer.getPowerModel(component); - if (powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE - && powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED) { - return false; - } - } - return true; - } - private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats, String batteryConsumerId) { for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { @@ -352,13 +288,14 @@ public class BatteryConsumerData { } } - private void addEntry(String title, EntryType entryType, double value1, double value2) { + private Entry addEntry(String title, EntryType entryType, double value1, double value2) { Entry entry = new Entry(); entry.title = title; entry.entryType = entryType; entry.value1 = value1; entry.value2 = value2; mEntries.add(entry); + return entry; } public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() { diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java index c6d71c3f573a..37d6b17a665c 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java @@ -24,6 +24,8 @@ import android.os.UidBatteryConsumer; import androidx.annotation.NonNull; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.List; class BatteryConsumerInfoHelper { @@ -76,6 +78,8 @@ class BatteryConsumerInfoHelper { String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain(); if (uid == Process.ROOT_UID) { info.label = "<root>"; + } else if (uid < Process.FIRST_APPLICATION_UID) { + info.label = makeSystemUidLabel(uid); } else { String[] packages = packageManager.getPackagesForUid(uid); String primaryPackageName = null; @@ -134,6 +138,23 @@ class BatteryConsumerInfoHelper { return info; } + private static CharSequence makeSystemUidLabel(int uid) { + for (Field field : Process.class.getDeclaredFields()) { + final int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) + && field.getType().equals(int.class) && field.getName().endsWith("_UID")) { + try { + if (uid == field.getInt(null)) { + String label = field.getName(); + return label.substring(0, label.lastIndexOf("_UID")); + } + } catch (IllegalAccessException ignored) { + } + } + } + return null; + } + private static BatteryConsumerInfo makeAggregateBatteryConsumerInfo( BatteryUsageStats batteryUsageStats) { BatteryConsumerInfo info = new BatteryConsumerInfo(); diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java index 4469168a77b4..3699690aca59 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java @@ -30,8 +30,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import androidx.activity.ComponentActivity; import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import androidx.recyclerview.widget.LinearLayoutManager; @@ -50,7 +50,7 @@ import java.util.Locale; * Picker, showing a sorted lists of applications and other types of entities consuming power. * Opens BatteryStatsViewerActivity upon item selection. */ -public class BatteryConsumerPickerActivity extends ComponentActivity { +public class BatteryConsumerPickerActivity extends AppCompatActivity { private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId"; private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000; private static final String FORCE_FRESH_STATS = "force_fresh_stats"; @@ -68,6 +68,7 @@ public class BatteryConsumerPickerActivity extends ComponentActivity { super.onCreate(icicle); setContentView(R.layout.battery_consumer_picker_layout); + setSupportActionBar(findViewById(R.id.toolbar)); mSwipeRefreshLayout = findViewById(R.id.swipe_refresh); mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_green_light); diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java index e165c49ff55d..350213161b7b 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java @@ -17,14 +17,18 @@ package com.android.frameworks.core.batterystatsviewer; import android.content.Context; +import android.os.BatteryConsumer; import android.os.BatteryStatsManager; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Bundle; +import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.TableLayout; +import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; @@ -40,6 +44,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.android.settingslib.utils.AsyncLoaderCompat; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -63,7 +68,16 @@ public class BatteryStatsViewerActivity extends ComponentActivity { private SwipeRefreshLayout mSwipeRefreshLayout; private View mCardView; private View mEmptyView; - private List<BatteryUsageStats> mBatteryUsageStats; + private BatteryUsageStats mBatteryUsageStats; + + private static SparseArray<String> sProcStateNames = new SparseArray<>(); + static { + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_UNSPECIFIED, "-"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_FOREGROUND, "FG"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_BACKGROUND, "BG"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, "FGS"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_CACHED, "Cached"); + } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -122,7 +136,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } private static class BatteryUsageStatsLoader extends - AsyncLoaderCompat<List<BatteryUsageStats>> { + AsyncLoaderCompat<BatteryUsageStats> { private final BatteryStatsManager mBatteryStatsManager; private final boolean mForceFreshStats; @@ -133,51 +147,44 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } @Override - public List<BatteryUsageStats> loadInBackground() { + public BatteryUsageStats loadInBackground() { final int maxStatsAgeMs = mForceFreshStats ? 0 : BATTERY_STATS_REFRESH_RATE_MILLIS; final BatteryUsageStatsQuery queryDefault = new BatteryUsageStatsQuery.Builder() - .includePowerModels() .includeProcessStateData() + .includeScreenStateData() + .includePowerStateData() .setMaxStatsAgeMs(maxStatsAgeMs) .build(); - final BatteryUsageStatsQuery queryPowerProfileModeledOnly = - new BatteryUsageStatsQuery.Builder() - .powerProfileModeledOnly() - .includePowerModels() - .includeProcessStateData() - .setMaxStatsAgeMs(maxStatsAgeMs) - .build(); - return mBatteryStatsManager.getBatteryUsageStats( - List.of(queryDefault, queryPowerProfileModeledOnly)); + return mBatteryStatsManager.getBatteryUsageStats(queryDefault); } @Override - protected void onDiscardResult(List<BatteryUsageStats> result) { + protected void onDiscardResult(BatteryUsageStats result) { } } private class BatteryUsageStatsLoaderCallbacks - implements LoaderCallbacks<List<BatteryUsageStats>> { + implements LoaderCallbacks<BatteryUsageStats> { @NonNull @Override - public Loader<List<BatteryUsageStats>> onCreateLoader(int id, Bundle args) { + public Loader<BatteryUsageStats> onCreateLoader(int id, Bundle args) { return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this, args.getBoolean(FORCE_FRESH_STATS)); } @Override - public void onLoadFinished(@NonNull Loader<List<BatteryUsageStats>> loader, - List<BatteryUsageStats> batteryUsageStats) { + public void onLoadFinished(@NonNull Loader<BatteryUsageStats> loader, + BatteryUsageStats batteryUsageStats) { onBatteryUsageStatsLoaded(batteryUsageStats); } @Override - public void onLoaderReset(@NonNull Loader<List<BatteryUsageStats>> loader) { + public void onLoaderReset(@NonNull Loader<BatteryUsageStats> loader) { } } - private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) { + private void onBatteryUsageStatsLoaded(BatteryUsageStats batteryUsageStats) { mBatteryUsageStats = batteryUsageStats; onBatteryStatsDataLoaded(); } @@ -238,10 +245,21 @@ public class BatteryStatsViewerActivity extends ComponentActivity { private static class BatteryStatsDataAdapter extends RecyclerView.Adapter<BatteryStatsDataAdapter.ViewHolder> { public static class ViewHolder extends RecyclerView.ViewHolder { + public static class SliceViewHolder { + public TableRow tableRow; + public int procState; + public int powerState; + public int screenState; + public TextView powerTextView; + public TextView durationTextView; + } + public ImageView iconImageView; public TextView titleTextView; public TextView value1TextView; public TextView value2TextView; + public TableLayout table; + public List<SliceViewHolder> slices = new ArrayList<>(); ViewHolder(View itemView) { super(itemView); @@ -250,6 +268,40 @@ public class BatteryStatsViewerActivity extends ComponentActivity { titleTextView = itemView.findViewById(R.id.title); value1TextView = itemView.findViewById(R.id.value1); value2TextView = itemView.findViewById(R.id.value2); + table = itemView.findViewById(R.id.table); + + for (int i = 0; i < sProcStateNames.size(); i++) { + int procState = sProcStateNames.keyAt(i); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_BATTERY, + BatteryConsumer.SCREEN_STATE_ON, + R.id.power_b_on, R.id.duration_b_on)); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_BATTERY, + BatteryConsumer.SCREEN_STATE_OTHER, + R.id.power_b_off, R.id.duration_b_off)); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_OTHER, + BatteryConsumer.SCREEN_STATE_ON, + R.id.power_c_on, R.id.duration_c_on)); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_OTHER, + BatteryConsumer.SCREEN_STATE_OTHER, + R.id.power_c_off, R.id.duration_c_off)); + } + } + + private SliceViewHolder createSliceViewHolder(int procState, int powerState, + int screenState, int powerTextViewResId, int durationTextViewResId) { + TableRow powerRow = table.findViewWithTag("procstate" + procState); + SliceViewHolder svh = new SliceViewHolder(); + svh.tableRow = powerRow; + svh.procState = procState; + svh.powerState = powerState; + svh.screenState = screenState; + svh.powerTextView = powerRow.findViewById(powerTextViewResId); + svh.durationTextView = powerRow.findViewById(durationTextViewResId); + return svh; } } @@ -269,62 +321,32 @@ public class BatteryStatsViewerActivity extends ComponentActivity { @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); - View itemView = layoutInflater.inflate(R.layout.battery_consumer_entry_layout, parent, - false); + ViewGroup itemView = (ViewGroup) layoutInflater.inflate( + R.layout.battery_consumer_entry_layout, parent, false); + TableLayout table = itemView.findViewById(R.id.table); + int offset = 1; // Skip header + for (int i = 0; i < sProcStateNames.size(); i++) { + View powerRow = layoutInflater.inflate(R.layout.battery_consumer_slices_layout, + itemView, false); + ((TextView) powerRow.findViewById(R.id.procState)) + .setText(sProcStateNames.valueAt(i)); + powerRow.setTag("procstate" + sProcStateNames.keyAt(i)); + table.addView(powerRow, offset++); + } + return new ViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { BatteryConsumerData.Entry entry = mEntries.get(position); - switch (entry.entryType) { - case UID_TOTAL_POWER: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_sum_24, 0); - setPowerText(viewHolder.value1TextView, entry.value1); - setProportionText(viewHolder.value2TextView, entry); - break; - case UID_POWER_PROFILE: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_calculate_24, - R.color.battery_consumer_bg_power_profile); - setPowerText(viewHolder.value1TextView, entry.value1); - setProportionText(viewHolder.value2TextView, entry); - break; - case UID_POWER_PROFILE_PROCESS_STATE: - setTitleIconAndBackground(viewHolder, " " + entry.title, - R.drawable.gm_calculate_24, - R.color.battery_consumer_bg_power_profile); - setPowerText(viewHolder.value1TextView, entry.value1); - viewHolder.value2TextView.setVisibility(View.INVISIBLE); - break; - case UID_POWER_ENERGY_CONSUMPTION: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - setProportionText(viewHolder.value2TextView, entry); - break; - case UID_POWER_ENERGY_PROCESS_STATE: - setTitleIconAndBackground(viewHolder, " " + entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - viewHolder.value2TextView.setVisibility(View.INVISIBLE); - break; - case UID_POWER_CUSTOM: + case UID: setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); + R.drawable.gm_energy_24, 0); setPowerText(viewHolder.value1TextView, entry.value1); setProportionText(viewHolder.value2TextView, entry); - break; - case UID_DURATION: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_timer_24, 0); - setDurationText(viewHolder.value1TextView, (long) entry.value1); - setProportionText(viewHolder.value2TextView, entry); + bindSlices(viewHolder, entry); break; case DEVICE_TOTAL_POWER: setTitleIconAndBackground(viewHolder, entry.title, @@ -332,27 +354,13 @@ public class BatteryStatsViewerActivity extends ComponentActivity { setPowerText(viewHolder.value1TextView, entry.value1); setPowerText(viewHolder.value2TextView, entry.value2); break; - case DEVICE_POWER_MODELED: + case DEVICE_POWER: setTitleIconAndBackground(viewHolder, entry.title, R.drawable.gm_calculate_24, R.color.battery_consumer_bg_power_profile); setPowerText(viewHolder.value1TextView, entry.value1); setPowerText(viewHolder.value2TextView, entry.value2); break; - case DEVICE_POWER_ENERGY_CONSUMPTION: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - setPowerText(viewHolder.value2TextView, entry.value2); - break; - case DEVICE_POWER_CUSTOM: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - setPowerText(viewHolder.value2TextView, entry.value2); - break; case DEVICE_DURATION: setTitleIconAndBackground(viewHolder, entry.title, R.drawable.gm_timer_24, 0); @@ -362,6 +370,65 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } } + private void bindSlices(ViewHolder viewHolder, BatteryConsumerData.Entry entry) { + if (entry.slices == null || entry.slices.isEmpty()) { + viewHolder.table.setVisibility(View.GONE); + return; + } + viewHolder.table.setVisibility(View.VISIBLE); + + boolean[] procStateRowPopulated = + new boolean[BatteryConsumer.PROCESS_STATE_COUNT]; + for (BatteryConsumerData.Slice s : entry.slices) { + if (s.powerMah != 0 || s.durationMs != 0) { + procStateRowPopulated[s.processState] = true; + } + } + + for (ViewHolder.SliceViewHolder sliceViewHolder : viewHolder.slices) { + BatteryConsumerData.Slice slice = null; + for (BatteryConsumerData.Slice s : entry.slices) { + if (s.powerState == sliceViewHolder.powerState + && s.screenState == sliceViewHolder.screenState + && s.processState == sliceViewHolder.procState) { + slice = s; + break; + } + } + if (!procStateRowPopulated[sliceViewHolder.procState]) { + sliceViewHolder.tableRow.setVisibility(View.GONE); + } else { + sliceViewHolder.tableRow.setVisibility(View.VISIBLE); + + if (slice != null && (slice.powerMah != 0 || slice.durationMs != 0)) { + sliceViewHolder.powerTextView.setText( + String.format(Locale.getDefault(), "%.1f", slice.powerMah)); + } else { + sliceViewHolder.powerTextView.setText(null); + } + + if (slice != null && slice.durationMs != 0) { + sliceViewHolder.durationTextView.setVisibility(View.VISIBLE); + String timeString; + if (slice.durationMs < MILLIS_IN_MINUTE) { + timeString = String.format(Locale.getDefault(), "%ds", + slice.durationMs / 1000); + } else if (slice.durationMs < 60 * MILLIS_IN_MINUTE) { + timeString = String.format(Locale.getDefault(), "%dm %ds", + slice.durationMs / MILLIS_IN_MINUTE, + (slice.durationMs % MILLIS_IN_MINUTE) / 1000); + } else { + timeString = String.format(Locale.getDefault(), "%dm", + slice.durationMs / MILLIS_IN_MINUTE); + } + sliceViewHolder.durationTextView.setText(timeString); + } else { + sliceViewHolder.durationTextView.setVisibility(View.GONE); + } + } + } + } + private void setTitleIconAndBackground(ViewHolder viewHolder, String title, int icon, int background) { viewHolder.titleTextView.setText(title); diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java index b01648838695..412169e7a905 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java @@ -42,5 +42,6 @@ public class TrampolineActivity extends Activity { private void launchMainActivity() { startActivity(new Intent(this, BatteryConsumerPickerActivity.class)); + finish(); } } diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index 2fc72e1d3994..177c7f0b2f27 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -26,6 +26,7 @@ import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDE import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; @@ -34,6 +35,8 @@ import static org.junit.Assert.fail; import android.annotation.SuppressLint; import android.app.PropertyInvalidatedCache.Args; +import android.app.PropertyInvalidatedCache.NonceWatcher; +import android.app.PropertyInvalidatedCache.NonceStore; import android.os.Binder; import com.android.internal.os.ApplicationSharedMemory; @@ -45,11 +48,15 @@ import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; +import com.android.internal.os.ApplicationSharedMemory; + import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import java.util.concurrent.TimeUnit; + /** * Test for verifying the behavior of {@link PropertyInvalidatedCache}. This test does * not use any actual binder calls - it is entirely self-contained. This test also relies @@ -490,6 +497,62 @@ public class PropertyInvalidatedCacheTests { } } + // Verify that NonceWatcher change reporting works properly + @Test + public void testNonceWatcherChanged() { + // Create a cache that will write a system nonce. + TestCache sysCache = new TestCache(MODULE_SYSTEM, "watcher1"); + sysCache.testPropertyName(); + + try (NonceWatcher watcher1 = sysCache.getNonceWatcher()) { + + // The property has never been invalidated so it is still unset. + assertFalse(watcher1.isChanged()); + + // Invalidate the cache. The first call to isChanged will return true but the second + // call will return false; + sysCache.invalidateCache(); + assertTrue(watcher1.isChanged()); + assertFalse(watcher1.isChanged()); + + // Invalidate the cache. The first call to isChanged will return true but the second + // call will return false; + sysCache.invalidateCache(); + sysCache.invalidateCache(); + assertTrue(watcher1.isChanged()); + assertFalse(watcher1.isChanged()); + + NonceWatcher watcher2 = sysCache.getNonceWatcher(); + // This watcher return isChanged() immediately because the nonce is not UNSET. + assertTrue(watcher2.isChanged()); + } + } + + // Verify that NonceWatcher wait-for-change works properly + @Test + public void testNonceWatcherWait() throws Exception { + // Create a cache that will write a system nonce. + TestCache sysCache = new TestCache(MODULE_TEST, "watcher1"); + + // Use the watcher outside a try-with-resources block. + NonceWatcher watcher1 = sysCache.getNonceWatcher(); + + // Invalidate the cache and then "wait". + sysCache.invalidateCache(); + assertEquals(watcher1.waitForChange(), 1); + + // Invalidate the cache three times and then "wait". + sysCache.invalidateCache(); + sysCache.invalidateCache(); + sysCache.invalidateCache(); + assertEquals(watcher1.waitForChange(), 3); + + // Wait for a change. It won't happen, but the code will time out after 10ms. + assertEquals(watcher1.waitForChange(10, TimeUnit.MILLISECONDS), 0); + + watcher1.close(); + } + // Verify the behavior of shared memory nonce storage. This does not directly test the cache // storing nonces in shared memory. @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED) @@ -502,10 +565,8 @@ public class PropertyInvalidatedCacheTests { // Create a server-side store and a client-side store. The server's store is mutable and // the client's store is not mutable. - PropertyInvalidatedCache.NonceStore server = - new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), true); - PropertyInvalidatedCache.NonceStore client = - new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), false); + NonceStore server = new NonceStore(shmem.getSystemNonceBlock(), true); + NonceStore client = new NonceStore(shmem.getSystemNonceBlock(), false); final String name1 = "name1"; assertEquals(server.getHandleForName(name1), INVALID_NONCE_INDEX); diff --git a/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java b/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java index 01c2abf2781b..a22eae3d19d9 100644 --- a/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java +++ b/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java @@ -15,13 +15,20 @@ */ package android.app.wallpaper; +import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE; +import static android.app.WallpaperManager.ORIENTATION_PORTRAIT; +import static android.app.WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE; +import static android.app.WallpaperManager.ORIENTATION_SQUARE_PORTRAIT; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import android.content.ComponentName; +import android.graphics.Rect; import android.net.Uri; import android.os.Parcel; import android.os.PersistableBundle; +import android.util.SparseArray; import android.util.Xml; import com.android.modules.utils.TypedXmlPullParser; @@ -41,17 +48,20 @@ import java.util.List; @RunWith(JUnit4.class) public class WallpaperDescriptionTest { - private static final String TAG = "WallpaperDescriptionTest"; + private static final Rect DEFAULT_CROP_PORTRAIT = new Rect(1, 2, 3, 4); + private static final Rect DEFAULT_CROP_LANDSCAPE = new Rect(5, 6, 7, 8); + private static final Rect DEFAULT_CROP_SQUARE_PORTRAIT = new Rect(9, 10, 11, 12); + private static final Rect DEFAULT_CROP_SQUARE_LANDSCAPE = new Rect(13, 14, 15, 16); private final ComponentName mTestComponent = new ComponentName("fakePackage", "fakeClass"); @Test public void equals_ignoresIrrelevantFields() { String id = "fakeId"; - WallpaperDescription desc1 = new WallpaperDescription.Builder().setComponent( - mTestComponent).setId(id).setTitle("fake one").build(); - WallpaperDescription desc2 = new WallpaperDescription.Builder().setComponent( - mTestComponent).setId(id).setTitle("fake different").build(); + WallpaperDescription desc1 = new WallpaperDescription.Builder() + .setComponent(mTestComponent).setId(id).setTitle("fake one").build(); + WallpaperDescription desc2 = new WallpaperDescription.Builder() + .setComponent(mTestComponent).setId(id).setTitle("fake different").build(); assertThat(desc1).isEqualTo(desc2); } @@ -72,13 +82,21 @@ public class WallpaperDescriptionTest { final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail"); final List<CharSequence> description = List.of("line1", "line2"); final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri"); - final PersistableBundle content = new PersistableBundle(); - content.putString("ckey", "cvalue"); + final PersistableBundle content = makeDefaultContent(); + final SparseArray<Rect> cropHints = makeDefaultCropHints(); + final float sampleSize = 0.9f; WallpaperDescription source = new WallpaperDescription.Builder() - .setComponent(mTestComponent).setId("fakeId").setThumbnail(thumbnail) - .setTitle("Fake title").setDescription(description) - .setContextUri(contextUri).setContextDescription("Context description") - .setContent(content).build(); + .setComponent(mTestComponent) + .setId("fakeId") + .setThumbnail(thumbnail) + .setTitle("Fake title") + .setDescription(description) + .setContextUri(contextUri) + .setContextDescription("Context description") + .setContent(content) + .setCropHints(cropHints) + .setSampleSize(sampleSize) + .build(); ByteArrayOutputStream ostream = new ByteArrayOutputStream(); TypedXmlSerializer serializer = Xml.newBinarySerializer(); @@ -122,6 +140,57 @@ public class WallpaperDescriptionTest { assertThat(destination.getContent()).isNotNull(); assertThat(destination.getContent().getString("ckey")).isEqualTo( source.getContent().getString("ckey")); + assertThat(destination.getCropHints()).isNotNull(); + assertThat(destination.getCropHints().get(ORIENTATION_PORTRAIT)).isEqualTo( + DEFAULT_CROP_PORTRAIT); + assertThat(destination.getCropHints().get(ORIENTATION_LANDSCAPE)).isEqualTo( + DEFAULT_CROP_LANDSCAPE); + assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_PORTRAIT)).isEqualTo( + DEFAULT_CROP_SQUARE_PORTRAIT); + assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_LANDSCAPE)).isEqualTo( + DEFAULT_CROP_SQUARE_LANDSCAPE); + assertThat(destination.getSampleSize()).isEqualTo(sampleSize); + } + + @Test + public void xml_roundTripSucceeds_withNulls() throws IOException, XmlPullParserException { + WallpaperDescription source = new WallpaperDescription.Builder().build(); + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + TypedXmlSerializer serializer = Xml.newBinarySerializer(); + serializer.setOutput(ostream, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + serializer.startTag(null, "test"); + source.saveToXml(serializer); + serializer.endTag(null, "test"); + serializer.endDocument(); + ostream.close(); + + WallpaperDescription destination = null; + ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray()); + TypedXmlPullParser parser = Xml.newBinaryPullParser(); + parser.setInput(istream, StandardCharsets.UTF_8.name()); + int type; + do { + type = parser.next(); + if (type == XmlPullParser.START_TAG && "test".equals(parser.getName())) { + destination = WallpaperDescription.restoreFromXml(parser); + } + } while (type != XmlPullParser.END_DOCUMENT); + + assertThat(destination).isNotNull(); + assertThat(destination.getComponent()).isEqualTo(source.getComponent()); + assertThat(destination.getId()).isEqualTo(source.getId()); + assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail()); + assertThat(destination.getTitle()).isNull(); + assertThat(destination.getDescription()).hasSize(0); + assertThat(destination.getContextUri()).isEqualTo(source.getContextUri()); + assertThat(destination.getContextDescription()).isNull(); + assertThat(destination.getContent()).isNotNull(); + assertThat(destination.getContent().keySet()).isEmpty(); + assertThat(destination.getCropHints()).isNotNull(); + assertThat(destination.getCropHints().size()).isEqualTo(0); + assertThat(destination.getSampleSize()).isEqualTo(source.getSampleSize()); } @Test @@ -129,13 +198,21 @@ public class WallpaperDescriptionTest { final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail"); final List<CharSequence> description = List.of("line1", "line2"); final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri"); - final PersistableBundle content = new PersistableBundle(); - content.putString("ckey", "cvalue"); - WallpaperDescription source = new WallpaperDescription.Builder().setComponent( - mTestComponent).setId("fakeId").setThumbnail(thumbnail).setTitle( - "Fake title").setDescription(description).setContextUri( - contextUri).setContextDescription("Context description").setContent( - content).build(); + final PersistableBundle content = makeDefaultContent(); + final SparseArray<Rect> cropHints = makeDefaultCropHints(); + final float sampleSize = 0.9f; + WallpaperDescription source = new WallpaperDescription.Builder() + .setComponent(mTestComponent) + .setId("fakeId") + .setThumbnail(thumbnail) + .setTitle("Fake title") + .setDescription(description) + .setContextUri(contextUri) + .setContextDescription("Context description") + .setContent(content) + .setCropHints(cropHints) + .setSampleSize(sampleSize) + .build(); Parcel parcel = Parcel.obtain(); source.writeToParcel(parcel, 0); @@ -162,6 +239,16 @@ public class WallpaperDescriptionTest { assertThat(destination.getContent()).isNotNull(); assertThat(destination.getContent().getString("ckey")).isEqualTo( source.getContent().getString("ckey")); + assertThat(destination.getCropHints()).isNotNull(); + assertThat(destination.getCropHints().get(ORIENTATION_PORTRAIT)).isEqualTo( + DEFAULT_CROP_PORTRAIT); + assertThat(destination.getCropHints().get(ORIENTATION_LANDSCAPE)).isEqualTo( + DEFAULT_CROP_LANDSCAPE); + assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_PORTRAIT)).isEqualTo( + DEFAULT_CROP_SQUARE_PORTRAIT); + assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_LANDSCAPE)).isEqualTo( + DEFAULT_CROP_SQUARE_LANDSCAPE); + assertThat(destination.getSampleSize()).isEqualTo(sampleSize); } @Test @@ -178,17 +265,14 @@ public class WallpaperDescriptionTest { assertThat(destination.getId()).isEqualTo(source.getId()); assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail()); assertThat(destination.getTitle()).isNull(); - assertThat(destination.getDescription()).hasSize(source.getDescription().size()); - for (int i = 0; i < destination.getDescription().size(); i++) { - CharSequence strDest = destination.getDescription().get(i); - CharSequence strSrc = source.getDescription().get(i); - assertWithMessage("description string mismatch") - .that(CharSequence.compare(strDest, strSrc)).isEqualTo(0); - } + assertThat(destination.getDescription()).hasSize(0); assertThat(destination.getContextUri()).isEqualTo(source.getContextUri()); assertThat(destination.getContextDescription()).isNull(); assertThat(destination.getContent()).isNotNull(); assertThat(destination.getContent().keySet()).isEmpty(); + assertThat(destination.getCropHints()).isNotNull(); + assertThat(destination.getCropHints().size()).isEqualTo(0); + assertThat(destination.getSampleSize()).isEqualTo(source.getSampleSize()); } @Test @@ -197,14 +281,22 @@ public class WallpaperDescriptionTest { final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail"); final List<CharSequence> description = List.of("line1", "line2"); final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri"); - final PersistableBundle content = new PersistableBundle(); - content.putString("ckey", "cvalue"); + final PersistableBundle content = makeDefaultContent(); + final SparseArray<Rect> cropHints = makeDefaultCropHints(); + final float sampleSize = 1.1f; final String destinationId = "destinationId"; - WallpaperDescription source = new WallpaperDescription.Builder().setComponent( - mTestComponent).setId(sourceId).setThumbnail(thumbnail).setTitle( - "Fake title").setDescription(description).setContextUri( - contextUri).setContextDescription("Context description").setContent( - content).build(); + WallpaperDescription source = new WallpaperDescription.Builder() + .setComponent(mTestComponent) + .setId(sourceId) + .setThumbnail(thumbnail) + .setTitle("Fake title") + .setDescription(description) + .setContextUri(contextUri) + .setContextDescription("Context description") + .setContent(content) + .setCropHints(cropHints) + .setSampleSize(sampleSize) + .build(); WallpaperDescription destination = source.toBuilder().setId(destinationId).build(); @@ -227,5 +319,29 @@ public class WallpaperDescriptionTest { assertThat(destination.getContent()).isNotNull(); assertThat(destination.getContent().getString("ckey")).isEqualTo( source.getContent().getString("ckey")); + assertThat(destination.getCropHints().get(ORIENTATION_PORTRAIT)).isEqualTo( + DEFAULT_CROP_PORTRAIT); + assertThat(destination.getCropHints().get(ORIENTATION_LANDSCAPE)).isEqualTo( + DEFAULT_CROP_LANDSCAPE); + assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_PORTRAIT)).isEqualTo( + DEFAULT_CROP_SQUARE_PORTRAIT); + assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_LANDSCAPE)).isEqualTo( + DEFAULT_CROP_SQUARE_LANDSCAPE); + assertThat(destination.getSampleSize()).isEqualTo(sampleSize); + } + + private static PersistableBundle makeDefaultContent() { + final PersistableBundle content = new PersistableBundle(); + content.putString("ckey", "cvalue"); + return content; + } + + private static SparseArray<Rect> makeDefaultCropHints() { + final SparseArray<Rect> cropHints = new SparseArray<>(); + cropHints.put(ORIENTATION_PORTRAIT, DEFAULT_CROP_PORTRAIT); + cropHints.put(ORIENTATION_LANDSCAPE, DEFAULT_CROP_LANDSCAPE); + cropHints.put(ORIENTATION_SQUARE_PORTRAIT, DEFAULT_CROP_SQUARE_PORTRAIT); + cropHints.put(ORIENTATION_SQUARE_LANDSCAPE, DEFAULT_CROP_SQUARE_LANDSCAPE); + return cropHints; } } diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java index e8c701ef2615..2a67716aa215 100644 --- a/core/tests/coretests/src/android/os/BuildTest.java +++ b/core/tests/coretests/src/android/os/BuildTest.java @@ -16,8 +16,10 @@ package android.os; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import android.platform.test.flag.junit.SetFlagsRule; @@ -103,4 +105,99 @@ public class BuildTest { mSetFlagsRule.disableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM); assertFalse(Flags.androidOsBuildVanillaIceCream()); } + + @Test + public void testParseFullVersionCorrectInputMajorDotMinor() throws Exception { + int version = Build.parseFullVersion("12.34"); + assertEquals(12, Build.getMajorSdkVersion(version)); + assertEquals(34, Build.getMinorSdkVersion(version)); + } + + @Test + public void testParseFullVersionCorrectInputOmitDotMinor() throws Exception { + int version = Build.parseFullVersion("1234"); + assertEquals(1234, Build.getMajorSdkVersion(version)); + assertEquals(0, Build.getMinorSdkVersion(version)); + } + + @Test + public void testParseFullVersionCorrectInputCurDevelopment() throws Exception { + int version = Build.parseFullVersion(Integer.toString(Build.VERSION_CODES.CUR_DEVELOPMENT)); + assertEquals(Build.VERSION_CODES.CUR_DEVELOPMENT, Build.getMajorSdkVersion(version)); + assertEquals(0, Build.getMinorSdkVersion(version)); + } + + @Test + public void testParseFullVersionIncorrectInputEmptyString() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion(""); + }); + } + + @Test + public void testParseFullVersionIncorrectInputNoNumbersInString() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion("foobar"); + }); + } + + @Test + public void testParseFullVersionIncorrectInputUnexpectedPatchVersion() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion("1.2.3"); + }); + } + + @Test + public void testParseFullVersionIncorrectInputLeadingDotMissingMajorVersion() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion(".1234"); + }); + } + + @Test + public void testParseFullVersionIncorrectInputTrailingDotMissingMinorVersion() + throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion("1234."); + }); + } + + @Test + public void testParseFullVersionIncorrectInputNegativeMajorVersion() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion("-12.34"); + }); + } + + @Test + public void testParseFullVersionIncorrectInputNegativeMinorVersion() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion("12.-34"); + }); + } + + @Test + public void testFullVersionToStringCorrectInput() throws Exception { + assertEquals("0.0", Build.fullVersionToString(0)); + assertEquals("1.0", Build.fullVersionToString(1 * 100000 + 0)); + assertEquals("1.1", Build.fullVersionToString(1 * 100000 + 1)); + assertEquals("12.34", Build.fullVersionToString(12 * 100000 + 34)); + } + + @Test + public void testFullVersionToStringSameStringAfterRoundTripViaParseFullVersion() + throws Exception { + String s = "12.34"; + int major = Build.getMajorSdkVersion(Build.parseFullVersion(s)); + int minor = Build.getMinorSdkVersion(Build.parseFullVersion(s)); + assertEquals(s, Build.fullVersionToString(major * 100000 + minor)); + } + + @Test + public void testFullVersionToStringIncorrectInputNegativeVersion() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.fullVersionToString(-1); + }); + } } diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS index 4620cb8d8148..c45080fb5e26 100644 --- a/core/tests/coretests/src/android/os/OWNERS +++ b/core/tests/coretests/src/android/os/OWNERS @@ -15,3 +15,6 @@ per-file IpcDataCache* = file:/PERFORMANCE_OWNERS # RemoteCallbackList per-file RemoteCallbackListTest.java = shayba@google.com + +# MessageQueue +per-file MessageQueueTest.java = mfasheh@google.com, shayba@google.com diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java index 726ee85dddd5..915ace058121 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java @@ -16,9 +16,9 @@ package android.view; -import static androidx.test.InstrumentationRegistry.getTargetContext; +import static android.view.flags.Flags.FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX; -import static com.google.common.truth.Truth.assertThat; +import static androidx.test.InstrumentationRegistry.getTargetContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -32,13 +32,16 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; import android.os.SystemClock; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -56,6 +59,8 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class ScrollCaptureSearchResultsTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final Rect EMPTY_RECT = new Rect(); private static final String TAG = "Test"; @@ -98,6 +103,45 @@ public class ScrollCaptureSearchResultsTest { assertNull("Expected null due to no valid targets", results.getTopResult()); } + /** + * A scrolling target should be excluded even when larger if it will be drawn over by another + * scrolling target. + */ + @EnableFlags(FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX) + @Test + public void testCoveredTargetsAreExcluded() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec); + callback1.setScrollBounds(new Rect(0, 0, 200, 200)); // 200 tall + View view1 = new FakeView(getTargetContext(), 0, 0, 200, 200, 1); + ScrollCaptureTarget target1 = createTargetWithView(view1, callback1, + new Rect(0, 0, 200, 200), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); + callback2.setScrollBounds(new Rect(0, 0, 200, 180)); // 180 tall + View view2 = new FakeView(getTargetContext(), 0, 20, 200, 200, 2); + ScrollCaptureTarget target2 = createTargetWithView(view2, callback2, + new Rect(0, 0, 200, 180), new Point(0, 20), View.SCROLL_CAPTURE_HINT_AUTO); + + // Top z-order but smaller, and non-intersecting. (positioned further Y than the first two) + FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec); + callback3.setScrollBounds(new Rect(0, 0, 50, 50)); + View view3 = new FakeView(getTargetContext(), 75, 250, 125, 300, 3); + ScrollCaptureTarget target3 = createTargetWithView(view3, callback3, + new Rect(0, 0, 50, 50), new Point(75, 250), View.SCROLL_CAPTURE_HINT_AUTO); + + results.addTarget(target1); + results.addTarget(target2); + results.addTarget(target3); + + assertTrue(results.isComplete()); + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Expected the second target because of higher z-Index", target2, result); + assertEquals("result has wrong scroll bounds", + new Rect(0, 0, 200, 180), result.getScrollBounds()); + } + @Test public void testSingleTarget() { ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); @@ -152,29 +196,29 @@ public class ScrollCaptureSearchResultsTest { // 2 - 10x10 + HINT_INCLUDE FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); - callback2.setScrollBounds(new Rect(0, 0, 10, 10)); - ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 0, 60, 60, 2); + callback2.setScrollBounds(new Rect(25, 25, 35, 35)); // 10x10 + ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 60, 60, 120, 2); ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_INCLUDE); // 3 - 20x20 + AUTO FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec); callback3.setScrollBounds(new Rect(0, 0, 20, 20)); - ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 0, 60, 60, 3); + ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 120, 60, 180, 3); ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); // 4 - 30x30 + AUTO FakeScrollCaptureCallback callback4 = new FakeScrollCaptureCallback(mDirectExec); callback4.setScrollBounds(new Rect(0, 0, 10, 10)); - ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 0, 60, 60, 4); + ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 180, 60, 240, 4); ScrollCaptureTarget target4 = createTargetWithView(targetView4, callback4, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); // 5 - 10x10 + child of #4 FakeScrollCaptureCallback callback5 = new FakeScrollCaptureCallback(mDirectExec); callback5.setScrollBounds(new Rect(0, 0, 10, 10)); - ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 60, 5); + ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 30, 5); ScrollCaptureTarget target5 = createTargetWithView(targetView5, callback5, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); targetView4.addView(targetView5); @@ -182,7 +226,7 @@ public class ScrollCaptureSearchResultsTest { // 6 - 20x20 + child of #4 FakeScrollCaptureCallback callback6 = new FakeScrollCaptureCallback(mDirectExec); callback6.setScrollBounds(new Rect(0, 0, 20, 20)); - ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 0, 60, 60, 6); + ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 30, 30, 60, 6); ScrollCaptureTarget target6 = createTargetWithView(targetView6, callback6, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); targetView4.addView(targetView6); @@ -194,20 +238,10 @@ public class ScrollCaptureSearchResultsTest { results.addTarget(target4); results.addTarget(target5); results.addTarget(target6); - assertTrue(results.isComplete()); + assertTrue("results.isComplete()", results.isComplete()); // Verify "top" result - assertEquals(target2, results.getTopResult()); - - // Verify priority ("best" first) - assertThat(results.getTargets()) - .containsExactly( - target2, - target6, - target5, - target4, - target3, - target1); + assertEquals("top result", target2, results.getTopResult()); } /** @@ -291,27 +325,22 @@ public class ScrollCaptureSearchResultsTest { new Rect(1, 2, 3, 4), result.getScrollBounds()); } - private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) { - view.setScrollCaptureHint(scrollCaptureHint); - view.onVisibilityAggregated(true); - // Treat any offset as padding, outset localVisibleRect on all sides and use this as - // child bounds - Rect bounds = new Rect(localVisibleRect); - bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top); - view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom); - view.onVisibilityAggregated(true); - } - private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { - View mockView = new View(getTargetContext()); + Rect bounds = new Rect(localVisibleRect); + // Use localVisibleRect as position, treat left/top offset as padding + bounds.left = 0; + bounds.top = 0; + View mockView = new FakeView(getTargetContext(), bounds.left, bounds.top, bounds.right, + bounds.bottom, View.NO_ID); return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow, scrollCaptureHint); } private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback, Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { - setupTargetView(view, localVisibleRect, scrollCaptureHint); + view.setScrollCaptureHint(scrollCaptureHint); + view.onVisibilityAggregated(true); return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback); } @@ -326,6 +355,32 @@ public class ScrollCaptureSearchResultsTest { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { } + + /** Ignores window attachment state. The standard impl always returns [0,0] if the view is + * not attached. This override allows testing without dealing with AttachInfo. + */ + @Override + public void getLocationInWindow(int[] outLocation) { + outLocation[0] = mLeft; + outLocation[1] = mTop; + ViewParent viewParent = getParent(); + while (viewParent instanceof View) { + final View view = (View) viewParent; + + outLocation[0] -= view.mScrollX; + outLocation[1] -= view.mScrollY; + + // Explicitly do not handle matrix/transforms, not needed for testing + if (!view.hasIdentityMatrix()) { + throw new IllegalStateException("This mock does not handle transforms!"); + } + + outLocation[0] += view.mLeft; + outLocation[1] += view.mTop; + + viewParent = view.mParent; + } + } } static class FakeScrollCaptureCallback implements ScrollCaptureCallback { diff --git a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java index 25608c328f95..adf7a720071a 100644 --- a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.flags.Flags.FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX; + import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; @@ -30,16 +32,20 @@ import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.annotation.NonNull; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; +import java.util.List; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -51,6 +57,9 @@ import java.util.function.Consumer; @RunWith(MockitoJUnitRunner.class) public class ViewGroupScrollCaptureTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final Executor DIRECT_EXECUTOR = Runnable::run; /** Make sure the hint flags are saved and loaded correctly. */ @@ -239,6 +248,56 @@ public class ViewGroupScrollCaptureTest { assertNull(results.getTopResult()); } + @EnableFlags(FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX) + @MediumTest + @Test + public void testDispatchScrollCaptureSearch_traversesInDrawingOrder() throws Exception { + final Context context = getInstrumentation().getContext(); + // Uses childDrawingOrder to reverse drawing order of children. + final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200); + + // w=200, h=180, z=10, drawn on top + final MockView view1 = new MockView(context, 0, 20, 200, 200); + TestScrollCaptureCallback callback1 = new TestScrollCaptureCallback(); + view1.setScrollCaptureCallback(callback1); + view1.setZ(10f); + + // w=200, h=200, z=0, drawn first, under view1 + final MockView view2 = new MockView(context, 0, 0, 200, 200); + TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback(); + view2.setScrollCaptureCallback(callback2); + + viewGroup.addView(view1); // test order is dependent on draw order by adding z=10 first + viewGroup.addView(view2); + + Rect localVisibleRect = new Rect(0, 0, 200, 200); + Point windowOffset = new Point(0, 0); + + // Where targets are added + final ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); + + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback1.completeSearchRequest(new Rect(0, 0, 200, 180)); + callback2.completeSearchRequest(new Rect(0, 0, 200, 200)); + assertTrue(results.isComplete()); + + List<ScrollCaptureTarget> targets = results.getTargets(); + List<View> targetViews = + targets.stream().map(ScrollCaptureTarget::getContainingView).toList(); + assertEquals(List.of(view2, view1), targetViews); + } + + static final class ReverseDrawingViewGroup extends MockViewGroup { + ReverseDrawingViewGroup(Context context, int left, int top, int right, int bottom) { + super(context, left, top, right, bottom, View.SCROLL_CAPTURE_HINT_AUTO); + } + + @Override + protected int getChildDrawingOrder(int childCount, int drawingPosition) { + return childCount == 0 ? 0 : childCount - (drawingPosition + 1); + } + } + /** * Test scroll capture search dispatch to child views. * <p> @@ -511,7 +570,7 @@ public class ViewGroupScrollCaptureTest { } }; - public static final class MockViewGroup extends ViewGroup { + public static class MockViewGroup extends ViewGroup { private ScrollCaptureCallback mInternalCallback; private Rect mOnScrollCaptureSearchLastLocalVisibleRect; private Point mOnScrollCaptureSearchLastWindowOffset; diff --git a/core/tests/coretests/src/android/widget/ProgressBarTest.java b/core/tests/coretests/src/android/widget/ProgressBarTest.java index fa6dd31923ac..0cbfaa97faef 100644 --- a/core/tests/coretests/src/android/widget/ProgressBarTest.java +++ b/core/tests/coretests/src/android/widget/ProgressBarTest.java @@ -23,8 +23,12 @@ import static org.junit.Assert.assertTrue; import android.app.Instrumentation; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.Flags; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -32,6 +36,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +45,10 @@ import org.junit.runner.RunWith; @Presubmit public class ProgressBarTest { private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); + + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); private ProgressBar mBar; private AccessibilityNodeInfo mInfo; @@ -181,4 +190,16 @@ public class ProgressBarTest { mBar.onInitializeAccessibilityNodeInfo(mInfo); assertEquals("custom state", mInfo.getStateDescription().toString()); } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_INDETERMINATE_RANGE_INFO) + public void testRangeInfo_indeterminateProgressBar_usesTypeIndeterminate() { + mBar.setIndeterminate(true); + assertTrue(mBar.isIndeterminate()); + + mBar.onInitializeAccessibilityNodeInfo(mInfo); + + assertEquals(mInfo.getRangeInfo().getType(), + AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INDETERMINATE); + } } diff --git a/core/tests/coretests/src/android/window/OWNERS b/core/tests/coretests/src/android/window/OWNERS index 6c80cf9e5945..b3fcea2907a1 100644 --- a/core/tests/coretests/src/android/window/OWNERS +++ b/core/tests/coretests/src/android/window/OWNERS @@ -1,2 +1,3 @@ include /services/core/java/com/android/server/wm/OWNERS -charlesccchen@google.com + +# Bug component: 1519745 = per-file WindowContext*,WindowMetrics*,WindowProvider*,WindowTokenClient*
\ No newline at end of file diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java index f1fbd559b8f8..21930d17ed68 100644 --- a/core/tests/coretests/src/android/window/WindowContextTest.java +++ b/core/tests/coretests/src/android/window/WindowContextTest.java @@ -29,6 +29,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import android.app.Activity; import android.app.EmptyActivity; @@ -60,6 +65,8 @@ import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; +import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -88,9 +95,21 @@ public class WindowContextTest { private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); private final WindowContext mWindowContext = createWindowContext(); private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService(); + private WindowTokenClientController mOriginalWindowTokenClientController; private static final int TIMEOUT_IN_SECONDS = 4; + @Before + public void setUp() { + // Keeping the original to set it back after each test, in case they applied any override. + mOriginalWindowTokenClientController = WindowTokenClientController.getInstance(); + } + + @After + public void tearDown() { + WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController); + } + @Test public void testCreateWindowContextWindowManagerAttachClientToken() { final WindowManager windowContextWm = WindowManagerImpl @@ -320,6 +339,20 @@ public class WindowContextTest { } } + @Test + public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() { + final WindowTokenClientController mockWindowTokenClientController = + mock(WindowTokenClientController.class); + WindowTokenClientController.overrideForTesting(mockWindowTokenClientController); + + mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1); + + verify(mockWindowTokenClientController).detachIfNeeded(any()); + verify(mockWindowTokenClientController).attachToDisplayArea(any(), + anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1), + any()); + } + private WindowContext createWindowContext() { return createWindowContext(TYPE_APPLICATION_OVERLAY); } diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index f78bc9294357..a6466c58dfda 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -16,7 +16,6 @@ package com.android.internal.accessibility; -import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; @@ -64,7 +63,6 @@ import android.os.Build; import android.os.Handler; import android.os.Message; import android.os.Vibrator; -import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; @@ -423,7 +421,6 @@ public class AccessibilityShortcutControllerTest { } @Test - @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); @@ -446,58 +443,6 @@ public class AccessibilityShortcutControllerTest { } @Test - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void testClickingDisableButtonInDialog_shouldClearShortcutId_old() throws Exception { - configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - configureValidShortcutService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - AccessibilityShortcutController.DialogStatus.NOT_SHOWN); - getController().performAccessibilityShortcut(); - - ArgumentCaptor<DialogInterface.OnClickListener> captor = - ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); - verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off), - captor.capture()); - captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE); - - assertThat( - Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE) - ).isEmpty(); - assertThat(Settings.Secure.getInt( - mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo( - AccessibilityShortcutController.DialogStatus.NOT_SHOWN); - } - - @Test - @EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE) - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void turnOffVolumeShortcutForAlwaysOnA11yService_shouldTurnOffA11yService() - throws Exception { - configureApplicationTargetSdkVersion(Build.VERSION_CODES.R); - turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ true); - - assertThat( - Settings.Secure.getString(mContentResolver, ENABLED_ACCESSIBILITY_SERVICES) - ).isEmpty(); - } - - @Test - @EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE) - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void turnOffVolumeShortcutForAlwaysOnA11yService_hasOtherTypesShortcut_shouldNotTurnOffA11yService() - throws Exception { - configureApplicationTargetSdkVersion(Build.VERSION_CODES.R); - Settings.Secure.putString( - mContentResolver, ACCESSIBILITY_BUTTON_TARGETS, SERVICE_NAME_STRING); - - turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ true); - - assertThat( - Settings.Secure.getString(mContentResolver, ENABLED_ACCESSIBILITY_SERVICES) - ).isEqualTo(SERVICE_NAME_STRING); - } - - @Test public void turnOffVolumeShortcutForStandardA11yService_shouldNotTurnOffA11yService() throws Exception { turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ false); @@ -530,7 +475,6 @@ public class AccessibilityShortcutControllerTest { } @Test - @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn() throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); @@ -554,30 +498,6 @@ public class AccessibilityShortcutControllerTest { } @Test - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn_old() - throws Exception { - configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - configureDefaultAccessibilityService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - AccessibilityShortcutController.DialogStatus.NOT_SHOWN); - getController().performAccessibilityShortcut(); - - ArgumentCaptor<DialogInterface.OnClickListener> captor = - ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); - verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.accessibility_shortcut_on), - captor.capture()); - captor.getValue().onClick(null, DialogInterface.BUTTON_NEGATIVE); - - assertThat(Settings.Secure.getString(mContentResolver, - ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING); - assertThat(Settings.Secure.getInt( - mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo( - AccessibilityShortcutController.DialogStatus.SHOWN); - } - - @Test - @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff() throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); @@ -601,29 +521,6 @@ public class AccessibilityShortcutControllerTest { } @Test - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff_old() - throws Exception { - configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - configureDefaultAccessibilityService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - AccessibilityShortcutController.DialogStatus.NOT_SHOWN); - getController().performAccessibilityShortcut(); - - ArgumentCaptor<DialogInterface.OnClickListener> captor = - ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); - verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off), - captor.capture()); - captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE); - - assertThat(Settings.Secure.getString(mContentResolver, - ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEmpty(); - assertThat(Settings.Secure.getInt( - mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo( - AccessibilityShortcutController.DialogStatus.NOT_SHOWN); - } - - @Test public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); @@ -638,9 +535,7 @@ public class AccessibilityShortcutControllerTest { } @Test - @EnableFlags({ - Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS, - Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE}) + @EnableFlags(Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void testOnAccessibilityShortcut_settingNull_dialogShown_enablesDefaultShortcut() throws Exception { configureDefaultAccessibilityService(); @@ -658,24 +553,6 @@ public class AccessibilityShortcutControllerTest { } @Test - @EnableFlags(Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void testOnAccessibilityShortcut_settingNull_dialogShown_writesDefaultSetting() - throws Exception { - configureDefaultAccessibilityService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - AccessibilityShortcutController.DialogStatus.SHOWN); - // Setting is only `null` during SUW. - Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null); - getController().performAccessibilityShortcut(); - - assertThat(Settings.Secure.getString(mContentResolver, - ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING); - verify(mAccessibilityManagerService).performAccessibilityShortcut( - Display.DEFAULT_DISPLAY, HARDWARE, null); - } - - @Test public void getFrameworkFeatureMap_shouldBeUnmodifiable() { final Map<ComponentName, AccessibilityShortcutController.FrameworkFeatureInfo> frameworkFeatureMap = diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java index 5339d915c8a4..acf7dbcaa5fe 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java @@ -33,12 +33,7 @@ import android.content.pm.ParceledListSlice; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; -import android.platform.test.flag.junit.SetFlagsRule; -import android.provider.Settings; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.Flags; import android.view.accessibility.IAccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -68,8 +63,6 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class InvisibleToggleAccessibilityServiceTargetTest { @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock private IAccessibilityManager mAccessibilityManagerService; @@ -117,7 +110,6 @@ public class InvisibleToggleAccessibilityServiceTargetTest { } @Test - @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) public void onCheckedChanged_true_callA11yManagerToUpdateShortcuts() throws Exception { mSut.onCheckedChanged(true); @@ -130,7 +122,6 @@ public class InvisibleToggleAccessibilityServiceTargetTest { } @Test - @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) public void onCheckedChanged_false_callA11yManagerToUpdateShortcuts() throws Exception { mSut.onCheckedChanged(false); verify(mAccessibilityManagerService).enableShortcutsForTargets( @@ -140,86 +131,4 @@ public class InvisibleToggleAccessibilityServiceTargetTest { anyInt()); assertThat(mListCaptor.getValue()).containsExactly(ALWAYS_ON_SERVICE_COMPONENT_NAME); } - - @Test - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void onCheckedChanged_turnOnShortcut_hasOtherShortcut_serviceKeepsOn() { - enableA11yService(/* enable= */ true); - addShortcutForA11yService( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ false); - addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ true); - - mSut.onCheckedChanged(/* isChecked= */ true); - - assertA11yServiceState(/* enabled= */ true); - } - - @Test - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void onCheckedChanged_turnOnShortcut_noOtherShortcut_shouldTurnOnService() { - enableA11yService(/* enable= */ false); - addShortcutForA11yService( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ false); - addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ false); - - mSut.onCheckedChanged(/* isChecked= */ true); - - assertA11yServiceState(/* enabled= */ true); - } - - @Test - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void onCheckedChanged_turnOffShortcut_hasOtherShortcut_serviceKeepsOn() { - enableA11yService(/* enable= */ true); - addShortcutForA11yService( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ true); - addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ true); - - mSut.onCheckedChanged(/* isChecked= */ false); - - assertA11yServiceState(/* enabled= */ true); - } - - @Test - @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS) - public void onCheckedChanged_turnOffShortcut_noOtherShortcut_shouldTurnOffService() { - enableA11yService(/* enable= */ true); - addShortcutForA11yService( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ true); - addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ false); - - mSut.onCheckedChanged(/* isChecked= */ false); - - assertA11yServiceState(/* enabled= */ false); - } - - private void enableA11yService(boolean enable) { - Settings.Secure.putString( - mContextSpy.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - enable ? ALWAYS_ON_SERVICE_COMPONENT_NAME : ""); - } - - private void addShortcutForA11yService(String shortcutKey, boolean add) { - Settings.Secure.putString( - mContextSpy.getContentResolver(), - shortcutKey, - add ? ALWAYS_ON_SERVICE_COMPONENT_NAME : ""); - } - - private void assertA11yServiceState(boolean enabled) { - if (enabled) { - assertThat( - Settings.Secure.getString( - mContextSpy.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES) - ).contains(ALWAYS_ON_SERVICE_COMPONENT_NAME); - } else { - assertThat( - Settings.Secure.getString( - mContextSpy.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES) - ).doesNotContain(ALWAYS_ON_SERVICE_COMPONENT_NAME); - } - } } diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java index e640ce5daf11..17fe15c94294 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java @@ -16,13 +16,17 @@ package android.hardware.devicestate; +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -40,7 +44,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; -import com.android.internal.util.ConcurrentUtils; import com.android.window.flags.Flags; import org.junit.Before; @@ -52,6 +55,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -103,7 +107,7 @@ public final class DeviceStateManagerGlobalTest { permissionEnforcer, true /* simulatePostCallback */); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); - dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback, never()).onDeviceStateChanged(any()); @@ -120,49 +124,68 @@ public final class DeviceStateManagerGlobalTest { final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); - dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test - public void registerCallback() { - final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); - final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); + public void registerCallback_usesExecutorForCallbacks() { + final DeviceStateCallback callback = mock(DeviceStateCallback.class); + final Executor executor = mock(Executor.class); + doAnswer(invocation -> { + Runnable runnable = (Runnable) invocation.getArguments()[0]; + runnable.run(); + return null; + }).when(executor).execute(any(Runnable.class)); - mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, - ConcurrentUtils.DIRECT_EXECUTOR); - mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, - ConcurrentUtils.DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, executor); + mService.setBaseState(OTHER_DEVICE_STATE); + mService.setSupportedStates(List.of(OTHER_DEVICE_STATE)); - // Verify initial callbacks - verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); - verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); - verify(callback2).onDeviceStateChanged(eq(mService.getMergedState())); + // Verify that the given executor is used for both initial and subsequent callbacks. + verify(executor, times(4)).execute(any(Runnable.class)); + } - reset(callback1); - reset(callback2); + @Test + public void registerCallback_supportedStatesChanged() { + final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); + final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); - // Change the supported states and verify callback + // Change the supported states and verify callback. mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE)); + verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); - mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE)); + } - reset(callback1); - reset(callback2); + @Test + public void registerCallback_baseStateChanged() { + final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); + final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); + mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE)); - // Change the base state and verify callback + // Change the base state and verify callback. mService.setBaseState(OTHER_DEVICE_STATE); + verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); verify(callback2).onDeviceStateChanged(eq(mService.getMergedState())); + } - reset(callback1); - reset(callback2); - - // Change the requested state and verify callback + @Test + public void registerCallback_requestedStateChanged() { + final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); + final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); final DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build(); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); + + // Change the requested state and verify callback. mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); @@ -173,8 +196,7 @@ public final class DeviceStateManagerGlobalTest { public void unregisterCallback() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal - .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); // Verify initial callbacks verify(callback).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); @@ -191,8 +213,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitRequest() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal - .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); @@ -212,8 +233,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseStateOverrideRequest() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal - .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); @@ -234,8 +254,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseAndEmulatedStateOverride() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal - .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); @@ -275,7 +294,7 @@ public final class DeviceStateManagerGlobalTest { final DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestState(request, - ConcurrentUtils.DIRECT_EXECUTOR /* executor */, + DIRECT_EXECUTOR /* executor */, callback /* callback */); verify(callback).onRequestActivated(eq(request)); diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 897fc543517e..dcaab8e645ac 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -607,6 +607,8 @@ applications that come with the platform <!-- Permission required for CTS test - IntrusionDetectionManagerTest --> <permission name="android.permission.READ_INTRUSION_DETECTION_STATE" /> <permission name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" /> + <!-- Permissions required for CTS test - BugreportManagerTest --> + <permission name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/errorprone/Android.bp b/errorprone/Android.bp index b559a15c3a60..1428b8965473 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -31,6 +31,14 @@ java_library_host { "//external/auto:auto_service_annotations", ], + javacflags: [ + // These exports are needed because this errorprone plugin access some private classes + // of the java compiler. + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + ], + plugins: [ "//external/auto:auto_service_plugin", ], diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index 5ea38431829e..223f9f6838ed 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -26,10 +26,12 @@ import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.hardware.devicestate.DeviceStateUtil; +import android.os.Looper; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; +import androidx.annotation.BinderThread; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -43,6 +45,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -69,38 +72,48 @@ public final class DeviceStateManagerFoldingFeatureProducer private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; @NonNull + private final Context mContext; + + @NonNull private final RawFoldingFeatureProducer mRawFoldSupplier; @NonNull private final DeviceStateMapper mDeviceStateMapper; - @VisibleForTesting - final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { - // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the getData() - // implementation. See https://errorprone.info/bugpattern/GuardedBy for limitations. - @SuppressWarnings("GuardedBy") - @MainThread + private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { + @BinderThread // Subsequent callback after registered. + @MainThread // Initial callback if registration is on the main thread. @Override public void onDeviceStateChanged(@NonNull DeviceState state) { - mCurrentDeviceState = state; - mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this - ::notifyFoldingFeatureChangeLocked); + final boolean isMainThread = Looper.myLooper() == Looper.getMainLooper(); + final Executor executor = isMainThread ? Runnable::run : mContext.getMainExecutor(); + executor.execute(() -> { + DeviceStateManagerFoldingFeatureProducer.this.onDeviceStateChanged(state); + }); } }; public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, @NonNull RawFoldingFeatureProducer rawFoldSupplier, @NonNull DeviceStateManager deviceStateManager) { + mContext = context; mRawFoldSupplier = rawFoldSupplier; mDeviceStateMapper = new DeviceStateMapper(context, deviceStateManager.getSupportedDeviceStates()); if (!mDeviceStateMapper.isDeviceStateToPostureMapEmpty()) { Objects.requireNonNull(deviceStateManager) - .registerCallback(context.getMainExecutor(), mDeviceStateCallback); + .registerCallback(Runnable::run, mDeviceStateCallback); } } + @MainThread + @VisibleForTesting + void onDeviceStateChanged(@NonNull DeviceState state) { + mCurrentDeviceState = state; + mRawFoldSupplier.getData(this::notifyFoldingFeatureChangeLocked); + } + /** * Add a callback to mCallbacks if there is no device state. This callback will be run * once a device state is set. Otherwise,run the callback immediately. @@ -118,9 +131,6 @@ public final class DeviceStateManagerFoldingFeatureProducer } } - // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the implementation of - // addDataChangedCallback(). See https://errorprone.info/bugpattern/GuardedBy for limitations. - @SuppressWarnings("GuardedBy") @Override protected void onListenersChanged() { super.onListenersChanged(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 6928409fd819..a5a84db32be7 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -182,8 +182,15 @@ class TaskContainer { @NonNull List<ParcelableSplitContainerData> getParcelableSplitContainerDataList() { - final List<ParcelableSplitContainerData> data = new ArrayList<>(mSplitContainers.size()); + final int size = + mSplitPinContainer != null ? mSplitContainers.size() - 1 : mSplitContainers.size(); + final List<ParcelableSplitContainerData> data = new ArrayList<>(size); for (SplitContainer splitContainer : mSplitContainers) { + if (splitContainer == mSplitPinContainer) { + // Skip SplitPinContainer as it cannot be restored because the SplitPinRule is + // set while pinning the container in runtime. + continue; + } data.add(splitContainer.getParcelableData()); } return data; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt index fb01cd88158c..ad29bf6acb1e 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt @@ -21,6 +21,7 @@ import android.content.res.Resources import android.hardware.devicestate.DeviceState import android.hardware.devicestate.DeviceStateManager import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry import androidx.window.common.layout.CommonFoldingFeature import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_FLAT import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_HALF_OPENED @@ -33,13 +34,14 @@ import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATU import com.android.internal.R import com.google.common.truth.Truth.assertThat import java.util.Optional +import java.util.concurrent.Executor import java.util.function.Consumer import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.stub @@ -69,14 +71,39 @@ class DeviceStateManagerFoldingFeatureProducerTest { } @Test - fun testRegisterCallback_usesMainExecutor() { + fun testRegisterCallback_initialCallbackOnMainThread_executesDirectly() { DeviceStateManagerFoldingFeatureProducer( mMockContext, mRawFoldSupplier, mMockDeviceStateManager, ) + val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>() + verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture()) - verify(mMockDeviceStateManager).registerCallback(eq(mMockContext.mainExecutor), any()) + InstrumentationRegistry.getInstrumentation().runOnMainSync { + callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) + } + + verify(mMockContext, never()).getMainExecutor() + } + + @Test + fun testRegisterCallback_subsequentCallbacks_postsToMainThread() { + val mockMainExecutor = mock<Executor>() + mMockContext.stub { + on { getMainExecutor() } doReturn mockMainExecutor + } + DeviceStateManagerFoldingFeatureProducer( + mMockContext, + mRawFoldSupplier, + mMockDeviceStateManager, + ) + val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>() + verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture()) + + callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) + + verify(mockMainExecutor).execute(any()) } @Test @@ -86,7 +113,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { mRawFoldSupplier, mMockDeviceStateManager, ) - ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) + ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) val currentData = ffp.getCurrentData() @@ -209,7 +236,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { mRawFoldSupplier, mMockDeviceStateManager, ) - ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) + ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) val storeFeaturesConsumer = mock<Consumer<List<CommonFoldingFeature>>>() ffp.getData(storeFeaturesConsumer) @@ -229,8 +256,8 @@ class DeviceStateManagerFoldingFeatureProducerTest { ffp.getData(storeFeaturesConsumer) verify(storeFeaturesConsumer, never()).accept(any()) - ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) - ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_OPENED) + ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) + ffp.onDeviceStateChanged(DEVICE_STATE_OPENED) verify(storeFeaturesConsumer).accept(HALF_OPENED_FOLDING_FEATURES) } diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml index e7ead63a8df6..62782a784db9 100644 --- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml +++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml @@ -22,14 +22,14 @@ android:gravity="bottom|end"> <include android:id="@+id/size_compat_hint" - android:visibility="invisible" + android:visibility="gone" android:layout_width="@dimen/compat_hint_width" android:layout_height="wrap_content" layout="@layout/compat_mode_hint"/> <ImageButton android:id="@+id/size_compat_restart_button" - android:visibility="invisible" + android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/compat_button_margin" diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml index 3dbf7542ac6e..fcf74e3c1936 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml @@ -46,15 +46,19 @@ <TextView android:id="@+id/application_name" android:layout_width="0dp" - android:layout_height="20dp" - android:maxWidth="86dp" + android:layout_height="wrap_content" + android:maxWidth="130dp" android:textAppearance="@android:style/TextAppearance.Material.Title" android:textSize="14sp" android:textFontWeight="500" - android:lineHeight="20dp" + android:lineHeight="20sp" android:layout_gravity="center_vertical" android:layout_weight="1" android:layout_marginStart="8dp" + android:singleLine="true" + android:ellipsize="none" + android:requiresFadingEdge="horizontal" + android:fadingEdgeLength="28dp" android:clickable="false" android:focusable="false" tools:text="Gmail"/> diff --git a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml index b5f04c3b815a..433d8546ece0 100644 --- a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml +++ b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml @@ -22,14 +22,14 @@ android:gravity="bottom|end"> <include android:id="@+id/user_aspect_ratio_settings_hint" - android:visibility="invisible" + android:visibility="gone" android:layout_width="@dimen/compat_hint_width" android:layout_height="wrap_content" layout="@layout/compat_mode_hint"/> <ImageButton android:id="@+id/user_aspect_ratio_settings_button" - android:visibility="invisible" + android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/compat_button_margin" diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 46ab090e310e..b72d25519e4f 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"فتح القائمة"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغيير الحجم"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"مجسَّم"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"استعادة"</string> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 10a33bb6aca7..c2d4d8b0613e 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvorite meni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promeni veličinu"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Imerzivne"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index d7da3aef02bb..7e804843dfce 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отваряне на менюто"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Преоразмеряване"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Реалистично"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Възстановяване"</string> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 4249373e0a3d..786ed769e7b7 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Obre el menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Canvia la mida"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiu"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaura"</string> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index a12534372135..99e9a8350822 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otevřít nabídku"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Změnit velikost"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Pohlcující"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnovit"</string> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index 85a44f6d760d..879347adf406 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Άνοιγμα μενού"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Αλλαγή μεγέθους"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Καθηλωτικό"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Επαναφορά"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 3e30ff048c6d..358e31476242 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index 0d7189bd16b3..923f30b9a5ba 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 3e30ff048c6d..358e31476242 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 3e30ff048c6d..358e31476242 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 6a1a2e5a4d39..7a2e8cffffcf 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir el menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Cambiar el tamaño"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Inmersivo"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restablecer"</string> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index f0d1d4e60392..9a15f90ac27e 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ava menüü"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Suuruse muutmine"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Kaasahaarav"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Taasta"</string> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index d10a02d75c18..eb50ba7c9477 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"باز کردن منو"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغییر اندازه"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمیتوان به اینجا منتقل کرد"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"فراگیر"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"بازیابی"</string> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 0eab10c34606..696650a11eb9 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"मेन्यू खोलें"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"साइज़ बदलें"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"इमर्सिव"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"वापस लाएं"</string> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 59a95f0c1393..7266942434c0 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Բացել ընտրացանկը"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Փոխել չափը"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Ներկայության էֆեկտով"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Վերականգնել"</string> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index b75c041afdab..8ea5c44006d6 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -79,7 +79,7 @@ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string> <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlla le bolle quando vuoi"</string> <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tocca Gestisci per disattivare le bolle dall\'app"</string> - <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> + <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nessuna bolla recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Le bolle recenti e ignorate appariranno qui"</string> <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatta utilizzando le bolle"</string> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 2960a19cef0a..8cc737216af4 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"メニューを開く"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"サイズ変更"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"没入モード"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"復元"</string> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index 9710e69a0418..fab0cb245cfd 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ເປີດເມນູ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ປັບຂະໜາດ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ສົມຈິງ"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ກູ້ຄືນ"</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 7e20ee1fc36b..c2e747c590d8 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"മെനു തുറക്കുക"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്ക്രീൻ വലുതാക്കുക"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"വലുപ്പം മാറ്റുക"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ഇമേഴ്സീവ്"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"പുനഃസ്ഥാപിക്കുക"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index b28b69080051..886ef7936665 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menu openen"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Formaat aanpassen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersief"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Herstellen"</string> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index e82916b89033..fa0e7c318f7e 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otwórz menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Zmień rozmiar"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Tryb immersyjny"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Przywróć"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index e0e91e715ed0..28dc7b0d228e 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Envolvente"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 063e47c80d5f..55452bd0e854 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Odpri meni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Spremeni velikost"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Poglobljeno"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnovi"</string> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index f74e460226b6..af8ac6898e83 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отворите мени"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Промени величину"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Имерзивне"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Врати"</string> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 7a5549eb5f59..0c3c18c70040 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Öppna menyn"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ändra storlek"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Uppslukande"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Återställ"</string> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index fe7556156e78..7be7373e03a9 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"เปิดเมนู"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ปรับขนาด"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"สมจริง"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"คืนค่า"</string> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index d9fb13bb0b6f..22b0174c0252 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Buksan ang Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"I-resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"I-restore"</string> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 3417fef84de5..c64b84373b17 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -133,8 +133,7 @@ <string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menyuni ochish"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string> - <!-- no translation found for desktop_mode_maximize_menu_snap_text (5673738963174074006) --> - <skip /> + <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Oʻlchamini oʻzgartirish"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string> <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiv"</string> <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Tiklash"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index e1ecd44eb69f..f3202a120492 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -125,7 +125,7 @@ <string name="select_text" msgid="5139083974039906583">"選取"</string> <string name="screenshot_text" msgid="1477704010087786671">"螢幕截圖"</string> <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string> - <string name="open_in_app_text" msgid="2874590745116268525">"在應用程式中開啟"</string> + <string name="open_in_app_text" msgid="2874590745116268525">"喺應用程式入面打開"</string> <string name="new_window_text" msgid="6318648868380652280">"新視窗"</string> <string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string> <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string> diff --git a/libs/WindowManager/Shell/shared/res/values/strings.xml b/libs/WindowManager/Shell/shared/res/values/strings.xml new file mode 100644 index 000000000000..d1804c04cbec --- /dev/null +++ b/libs/WindowManager/Shell/shared/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- Accessibility text for the icons generated by the Manage Windows submenu + in desktop mode and taskbar. [CHAR LIMIT=NONE] --> + <string name="manage_windows_icon_text">Open Window %1$d</string> +</resources> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt index f14dfdbc7d30..0954b52ff151 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.shared.desktopmode +package com.android.wm.shell.shared.multiinstance import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet @@ -34,6 +34,7 @@ import android.view.View.SCALE_Y import android.view.ViewGroup.MarginLayoutParams import android.widget.LinearLayout import android.window.TaskSnapshot +import com.android.wm.shell.shared.R /** * View for the All Windows menu option, used by both Desktop Windowing and Taskbar. @@ -167,6 +168,9 @@ abstract class ManageWindowsViewContainer( val appSnapshotButton = SurfaceView(context) appSnapshotButton.cornerRadius = iconRadius appSnapshotButton.setZOrderOnTop(true) + appSnapshotButton.contentDescription = context.resources.getString( + R.string.manage_windows_icon_text, iconCount + 1 + ) appSnapshotButton.setOnClickListener { onIconClickListener?.invoke(taskId) } @@ -230,10 +234,12 @@ abstract class ManageWindowsViewContainer( /** Play the animation for opening the menu. */ fun animateOpen() { animateView(rootView, MENU_BOUNDS_SHRUNK_SCALE, MENU_BOUNDS_FULL_SCALE, - MENU_START_ALPHA, MENU_FULL_ALPHA) + MENU_START_ALPHA, MENU_FULL_ALPHA + ) for (view in iconViews) { animateView(view, MENU_BOUNDS_SHRUNK_SCALE, MENU_BOUNDS_FULL_SCALE, - MENU_START_ALPHA, MENU_FULL_ALPHA) + MENU_START_ALPHA, MENU_FULL_ALPHA + ) } createAnimatorSet().start() } @@ -241,10 +247,12 @@ abstract class ManageWindowsViewContainer( /** Play the animation for closing the menu. */ fun animateClose(callback: () -> Unit) { animateView(rootView, MENU_BOUNDS_FULL_SCALE, MENU_BOUNDS_SHRUNK_SCALE, - MENU_FULL_ALPHA, MENU_START_ALPHA) + MENU_FULL_ALPHA, MENU_START_ALPHA + ) for (view in iconViews) { animateView(view, MENU_BOUNDS_FULL_SCALE, MENU_BOUNDS_SHRUNK_SCALE, - MENU_FULL_ALPHA, MENU_START_ALPHA) + MENU_FULL_ALPHA, MENU_START_ALPHA + ) } createAnimatorSet().apply { addListener( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index b82496e45415..3b53c3fbe03f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -274,8 +274,11 @@ public class BubbleController implements ConfigurationChangeListener, private final DragAndDropController mDragAndDropController; /** Used to send bubble events to launcher. */ private Bubbles.BubbleStateListener mBubbleStateListener; - /** Used to track previous navigation mode to detect switch to buttons navigation. */ - private boolean mIsPrevNavModeGestures; + /** + * Used to track previous navigation mode to detect switch to buttons navigation. Set to + * true to switch the bubble bar to the opposite side for 3 nav buttons mode on device boot. + */ + private boolean mIsPrevNavModeGestures = true; /** Used to send updates to the views from {@link #mBubbleDataListener}. */ private BubbleViewCallback mBubbleViewCallback; @@ -357,7 +360,6 @@ public class BubbleController implements ConfigurationChangeListener, } }; mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(this); - mIsPrevNavModeGestures = ContextUtils.isGestureNavigationMode(mContext); } private void registerOneHandedState(OneHandedController oneHanded) { @@ -593,9 +595,9 @@ public class BubbleController implements ConfigurationChangeListener, if (mBubbleStateListener != null) { boolean isCurrentNavModeGestures = ContextUtils.isGestureNavigationMode(mContext); if (mIsPrevNavModeGestures && !isCurrentNavModeGestures) { - BubbleBarLocation navButtonsLocation = ContextUtils.isRtl(mContext) + BubbleBarLocation bubbleBarLocation = ContextUtils.isRtl(mContext) ? BubbleBarLocation.RIGHT : BubbleBarLocation.LEFT; - mBubblePositioner.setBubbleBarLocation(navButtonsLocation); + mBubblePositioner.setBubbleBarLocation(bubbleBarLocation); } mIsPrevNavModeGestures = isCurrentNavModeGestures; BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/transition/TransitionStateHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/transition/TransitionStateHolder.kt new file mode 100644 index 000000000000..4dda2a894253 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/transition/TransitionStateHolder.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.transition + +import com.android.wm.shell.dagger.WMSingleton +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState +import com.android.wm.shell.recents.RecentsTransitionStateListener.isRunning +import com.android.wm.shell.sysui.ShellInit +import javax.inject.Inject + +/** + * Holder for the state of the transitions. + */ +@WMSingleton +class TransitionStateHolder @Inject constructor( + shellInit: ShellInit, + private val recentsTransitionHandler: RecentsTransitionHandler +) { + + @Volatile + @RecentsTransitionState + private var recentsTransitionState: Int = + RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING + + init { + shellInit.addInitCallback({ onInit() }, this) + } + + fun isRecentsTransitionRunning(): Boolean = isRunning(recentsTransitionState) + + private fun onInit() { + recentsTransitionHandler.addTransitionStateListener( + object : RecentsTransitionStateListener { + override fun onTransitionStateChanged(@RecentsTransitionState state: Int) { + recentsTransitionState = state + } + } + ) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java index 49c2785f1ecd..688f8ca2dc75 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java @@ -68,7 +68,7 @@ class CompatUILayout extends LinearLayout { private void setViewVisibility(@IdRes int resId, boolean show) { final View view = findViewById(resId); - int visibility = show ? View.VISIBLE : View.INVISIBLE; + int visibility = show ? View.VISIBLE : View.GONE; if (view.getVisibility() == visibility) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java index fd1bbc477cf4..b141bebbe8b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java @@ -100,7 +100,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout { private void setViewVisibility(@IdRes int resId, boolean show) { final View view = findViewById(resId); - int visibility = show ? View.VISIBLE : View.INVISIBLE; + int visibility = show ? View.VISIBLE : View.GONE; if (view.getVisibility() == visibility) { return; } @@ -171,7 +171,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout { fadeOut.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - view.setVisibility(View.INVISIBLE); + view.setVisibility(View.GONE); } }); fadeOut.start(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt index 0ac7aff306a0..523e2f5cf7dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt @@ -18,133 +18,51 @@ package com.android.wm.shell.compatui.letterbox import android.graphics.Rect import android.view.SurfaceControl -import com.android.internal.protolog.ProtoLog -import com.android.wm.shell.dagger.WMSingleton -import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT -import javax.inject.Inject +import android.view.SurfaceControl.Transaction /** - * Component responsible for handling the lifecycle of the letterbox surfaces. + * Abstracts the component responsible to handle a single or multiple letterbox surfaces for a + * specific [Change]. */ -@WMSingleton -class LetterboxController @Inject constructor( - private val letterboxConfiguration: LetterboxConfiguration -) { - - companion object { - /* - * Letterbox surfaces need to stay below the activity layer which is 0. - */ - // TODO(b/378673153): Consider adding this to [TaskConstants]. - @JvmStatic - private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 - @JvmStatic - private val TAG = "LetterboxController" - } - - private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxItem>() +interface LetterboxController { /** * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. */ fun createLetterboxSurface( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction, + transaction: Transaction, parentLeash: SurfaceControl - ) { - letterboxMap.runOnItem(key, onMissed = { k, m -> - m[k] = LetterboxItem( - SurfaceControl.Builder() - .setName("ShellLetterboxSurface-$key") - .setHidden(true) - .setColorLayer() - .setParent(parentLeash) - .setCallsite("LetterboxController-createLetterboxSurface") - .build().apply { - startTransaction.setLayer( - this, - TASK_CHILD_LAYER_LETTERBOX_BACKGROUND - ).setColorSpaceAgnostic(this, true) - .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) - } - ) - }) - } + ) /** * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. */ fun destroyLetterboxSurface( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction - ) { - letterboxMap.runOnItem(key, onFound = { item -> - item.fullWindowSurface?.run { - startTransaction.remove(this) - } - }) - letterboxMap.remove(key) - } + transaction: Transaction + ) /** * Invoked to show/hide the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceVisibility( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction, - visible: Boolean = true - ) { - letterboxMap.runOnItem(key, onFound = { item -> - item.fullWindowSurface?.run { - startTransaction.setVisibility(this, visible) - } - }) - } + transaction: Transaction, + visible: Boolean + ) /** * Updates the bounds for the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceBounds( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction, - bounds: Rect - ) { - letterboxMap.runOnItem(key, onFound = { item -> - item.fullWindowSurface?.run { - startTransaction.moveAndCrop(this, bounds) - } - }) - } + transaction: Transaction, + taskBounds: Rect + ) - /* - * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present. + /** + * Utility method to dump the current state. */ - private fun MutableMap<LetterboxKey, LetterboxItem>.runOnItem( - key: LetterboxKey, - onFound: (LetterboxItem) -> Unit = { _ -> }, - onMissed: ( - LetterboxKey, - MutableMap<LetterboxKey, LetterboxItem> - ) -> Unit = { _, _ -> } - ) { - this[key]?.let { - return onFound(it) - } - return onMissed(key, this) - } - - fun dump() { - ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") - } - - private fun SurfaceControl.Transaction.moveAndCrop( - surface: SurfaceControl, - rect: Rect - ): SurfaceControl.Transaction = - setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) - .setWindowCrop( - surface, - rect.width(), - rect.height() - ) + fun dump() } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt index 98fd2472f1e4..adb034cc4787 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt @@ -16,10 +16,5 @@ package com.android.wm.shell.compatui.letterbox -import android.view.SurfaceControl - // The key to use for identify the letterbox sessions. -data class LetterboxKey(val displayId: Int, val taskId: Int) - -// Encapsulate the objects for the specific letterbox session. -data class LetterboxItem(val fullWindowSurface: SurfaceControl?)
\ No newline at end of file +data class LetterboxKey(val displayId: Int, val taskId: Int)
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt new file mode 100644 index 000000000000..e88d91f3956c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui.letterbox + +import android.view.SurfaceControl +import com.android.wm.shell.dagger.WMSingleton +import javax.inject.Inject + +/** + * Component responsible for the actual creation of the Letterbox surfaces. + */ +@WMSingleton +class LetterboxSurfaceBuilder @Inject constructor( + private val letterboxConfiguration: LetterboxConfiguration +) { + + companion object { + /* + * Letterbox surfaces need to stay below the activity layer which is 0. + */ + // TODO(b/378673153): Consider adding this to [TaskConstants]. + @JvmStatic + private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 + } + + fun createSurface( + tx: SurfaceControl.Transaction, + parentLeash: SurfaceControl, + surfaceName: String, + callSite: String, + surfaceBuilder: SurfaceControl.Builder = SurfaceControl.Builder() + ) = surfaceBuilder + .setName(surfaceName) + .setHidden(true) + .setColorLayer() + .setParent(parentLeash) + .setCallsite(callSite) + .build().apply { + tx.setLayer( + this, + TASK_CHILD_LAYER_LETTERBOX_BACKGROUND + ).setColorSpaceAgnostic(this, true) + .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt index 67429bdd112b..b50716ad07a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt @@ -43,12 +43,7 @@ class LetterboxTransitionObserver( init { if (appCompatRefactoring()) { - ProtoLog.v( - WM_SHELL_APP_COMPAT, - "%s: %s", - TAG, - "Initializing LetterboxTransitionObserver" - ) + logV("Initializing LetterboxTransitionObserver") shellInit.addInitCallback({ transitions.registerObserver(this) }, this) @@ -69,38 +64,45 @@ class LetterboxTransitionObserver( for (change in info.changes) { change.taskInfo?.let { ti -> val key = LetterboxKey(ti.displayId, ti.taskId) - if (isClosingType(change.mode)) { - letterboxController.destroyLetterboxSurface( - key, - startTransaction - ) - } else { - val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed - if (isTopActivityLetterboxed) { - letterboxController.createLetterboxSurface( + val taskBounds = Rect( + change.endRelOffset.x, + change.endRelOffset.y, + change.endAbsBounds.width(), + change.endAbsBounds.height() + ) + with(letterboxController) { + if (isClosingType(change.mode)) { + destroyLetterboxSurface( key, - startTransaction, - change.leash + startTransaction ) - letterboxController.updateLetterboxSurfaceBounds( + } else { + val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed + if (isTopActivityLetterboxed) { + createLetterboxSurface( + key, + startTransaction, + change.leash + ) + updateLetterboxSurfaceBounds( + key, + startTransaction, + taskBounds + ) + } + updateLetterboxSurfaceVisibility( key, startTransaction, - Rect( - change.endRelOffset.x, - change.endRelOffset.y, - change.endAbsBounds.width(), - change.endAbsBounds.height() - ) + isTopActivityLetterboxed ) } - letterboxController.updateLetterboxSurfaceVisibility( - key, - startTransaction, - isTopActivityLetterboxed - ) + dump() } - letterboxController.dump() } } } + + private fun logV(msg: String) { + ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, msg) + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt new file mode 100644 index 000000000000..f21a7272287e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt @@ -0,0 +1,136 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui.letterbox + +import android.graphics.Rect +import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction +import com.android.internal.protolog.ProtoLog +import com.android.wm.shell.dagger.WMSingleton +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT +import javax.inject.Inject + +/** + * Component responsible for handling the lifecycle of a single letterbox surface. + */ +@WMSingleton +class SingleSurfaceLetterboxController @Inject constructor( + private val letterboxBuilder: LetterboxSurfaceBuilder +) : LetterboxController { + + companion object { + @JvmStatic + private val TAG = "LetterboxController" + } + + private val letterboxMap = mutableMapOf<LetterboxKey, SurfaceControl>() + + /** + * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. + */ + override fun createLetterboxSurface( + key: LetterboxKey, + transaction: Transaction, + parentLeash: SurfaceControl + ) { + letterboxMap.runOnItem(key, onMissed = { k, m -> + m[k] = letterboxBuilder.createSurface( + transaction, + parentLeash, + surfaceName = "ShellLetterboxSurface-$key", + callSite = "LetterboxController-createLetterboxSurface" + ) + }) + } + + /** + * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. + */ + override fun destroyLetterboxSurface( + key: LetterboxKey, + transaction: Transaction + ) { + letterboxMap.runOnItem(key, onFound = { item -> + item.run { + transaction.remove(this) + } + }) + letterboxMap.remove(key) + } + + /** + * Invoked to show/hide the letterbox surfaces for given displayId/taskId. + */ + override fun updateLetterboxSurfaceVisibility( + key: LetterboxKey, + transaction: Transaction, + visible: Boolean + ) { + letterboxMap.runOnItem(key, onFound = { item -> + item.run { + transaction.setVisibility(this, visible) + } + }) + } + + /** + * Updates the bounds for the letterbox surfaces for given displayId/taskId. + */ + override fun updateLetterboxSurfaceBounds( + key: LetterboxKey, + transaction: Transaction, + taskBounds: Rect + ) { + letterboxMap.runOnItem(key, onFound = { item -> + item.run { + transaction.moveAndCrop(this, taskBounds) + } + }) + } + + override fun dump() { + ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") + } + + /* + * Executes [onFound] on the [SurfaceControl] if present or [onMissed] if not present. + */ + private fun MutableMap<LetterboxKey, SurfaceControl>.runOnItem( + key: LetterboxKey, + onFound: (SurfaceControl) -> Unit = { _ -> }, + onMissed: ( + LetterboxKey, + MutableMap<LetterboxKey, SurfaceControl> + ) -> Unit = { _, _ -> } + ) { + this[key]?.let { + return onFound(it) + } + return onMissed(key, this) + } + + private fun Transaction.moveAndCrop( + surface: SurfaceControl, + rect: Rect + ): Transaction = + setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) + .setWindowCrop( + surface, + rect.width(), + rect.height() + ) +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 0f636588476a..974535385334 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -71,6 +71,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler; import com.android.wm.shell.compatui.letterbox.LetterboxController; import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver; +import com.android.wm.shell.compatui.letterbox.SingleSurfaceLetterboxController; import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler; @@ -854,14 +855,15 @@ public abstract class WMShellModule { Optional<DesktopTasksController> desktopTasksController, InputManager inputManager, ShellTaskOrganizer shellTaskOrganizer, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + @ShellMainThread ShellExecutor mainExecutor) { if (DesktopModeStatus.canEnterDesktopMode(context) && useKeyGestureEventHandler() && manageKeyGestures() && (Flags.enableMoveToNextDisplayShortcut() || Flags.enableTaskResizingKeyboardShortcuts())) { return Optional.of(new DesktopModeKeyGestureHandler(context, desktopModeWindowDecorViewModel, desktopTasksController, - inputManager, shellTaskOrganizer, focusTransitionObserver)); + inputManager, shellTaskOrganizer, focusTransitionObserver, mainExecutor)); } return Optional.empty(); } @@ -885,6 +887,7 @@ public abstract class WMShellModule { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, + Optional<DesktopImmersiveController> desktopImmersiveController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, @@ -906,6 +909,7 @@ public abstract class WMShellModule { mainChoreographer, bgExecutor, shellInit, shellCommandHandler, windowManager, taskOrganizer, desktopRepository, displayController, shellController, displayInsetsController, syncQueue, transitions, desktopTasksController, + desktopImmersiveController.get(), rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, assistContentRequester, multiInstanceHelper, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, @@ -1316,4 +1320,9 @@ public abstract class WMShellModule { ) { return new LetterboxTransitionObserver(shellInit, transitions, letterboxController); } + + @WMSingleton + @Binds + abstract LetterboxController bindsLetterboxController( + SingleSurfaceLetterboxController letterboxController); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index 3508ecee6d51..7507e0458c48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.Context; import android.os.Handler; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; @@ -38,6 +39,7 @@ import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; +import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.pip2.phone.PhonePipMenuController; import com.android.wm.shell.pip2.phone.PipController; import com.android.wm.shell.pip2.phone.PipMotionHelper; @@ -128,8 +130,11 @@ public abstract class Pip2Module { static PipScheduler providePipScheduler(Context context, PipBoundsState pipBoundsState, @ShellMainThread ShellExecutor mainExecutor, - PipTransitionState pipTransitionState) { - return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState); + PipTransitionState pipTransitionState, + Optional<DesktopRepository> desktopRepositoryOptional, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState, + desktopRepositoryOptional, rootTaskDisplayAreaOrganizer); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt index 4723eb273988..dd95273dd4f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt @@ -99,6 +99,7 @@ class DesktopImmersiveController( /** Starts a transition to enter full immersive state inside the desktop. */ fun moveTaskToImmersive(taskInfo: RunningTaskInfo) { + check(taskInfo.isFreeform) { "Task must already be in freeform" } if (inProgress) { logV( "Cannot start entry because transition(s) already in progress: %s", @@ -121,6 +122,7 @@ class DesktopImmersiveController( /** Starts a transition to move an immersive task out of immersive. */ fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo, reason: ExitReason) { + check(taskInfo.isFreeform) { "Task must already be in freeform" } if (inProgress) { logV( "Cannot start exit because transition(s) already in progress: %s", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt index 6cb23b81f1e4..43544f6fd4b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt @@ -30,9 +30,11 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.Context import com.android.hardware.input.Flags.manageKeyGestures import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts +import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.shared.annotations.ShellMainThread import java.util.Optional /** @@ -45,6 +47,7 @@ class DesktopModeKeyGestureHandler( inputManager: InputManager, private val shellTaskOrganizer: ShellTaskOrganizer, private val focusTransitionObserver: FocusTransitionObserver, + @ShellMainThread private val mainExecutor: ShellExecutor, ) : KeyGestureEventHandler { init { @@ -69,42 +72,52 @@ class DesktopModeKeyGestureHandler( KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW -> { logV("Key gesture SNAP_LEFT_FREEFORM_WINDOW is handled") getGloballyFocusedFreeformTask()?.let { - desktopModeWindowDecorViewModel.get().onSnapResize( - it.taskId, - true, - DesktopModeEventLogger.Companion.InputMethod.KEYBOARD - ) + mainExecutor.execute { + desktopModeWindowDecorViewModel.get().onSnapResize( + it.taskId, + true, + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, + /* fromMenu= */ false + ) + } } return true } KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW -> { logV("Key gesture SNAP_RIGHT_FREEFORM_WINDOW is handled") getGloballyFocusedFreeformTask()?.let { - desktopModeWindowDecorViewModel.get().onSnapResize( - it.taskId, - false, - DesktopModeEventLogger.Companion.InputMethod.KEYBOARD - ) + mainExecutor.execute { + desktopModeWindowDecorViewModel.get().onSnapResize( + it.taskId, + false, + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, + /* fromMenu= */ false + ) + } } return true } KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW -> { logV("Key gesture TOGGLE_MAXIMIZE_FREEFORM_WINDOW is handled") getGloballyFocusedFreeformTask()?.let { - desktopTasksController.get().toggleDesktopTaskSize( - it, - ResizeTrigger.MAXIMIZE_MENU, - DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, - ) + mainExecutor.execute { + desktopTasksController.get().toggleDesktopTaskSize( + it, + ResizeTrigger.MAXIMIZE_MENU, + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, + ) + } } return true } KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> { logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled") getGloballyFocusedFreeformTask()?.let { - desktopTasksController.get().minimizeTask( - it, - ) + mainExecutor.execute { + desktopTasksController.get().minimizeTask( + it, + ) + } } return true } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt index d0e01625a3aa..2c432bcb55ab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt @@ -124,7 +124,19 @@ class DesktopModeUiEventLogger( @UiEvent(doc = "Drag the window header to an edge to tile it to the left side") DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_LEFT(2006), @UiEvent(doc = "Drag the window header to an edge to tile it to the right side") - DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_RIGHT(2007); + DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_RIGHT(2007), + @UiEvent(doc = "Hover or long press the maximize button to reveal the menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_REVEAL_MENU(2015), + @UiEvent(doc = "Tap on the maximize option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_MAXIMIZE(2009), + @UiEvent(doc = "Tap on the immersive option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_IMMERSIVE(2010), + @UiEvent(doc = "Tap on the restore option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_RESTORE(2011), + @UiEvent(doc = "Tap on the tile to left option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_LEFT(2012), + @UiEvent(doc = "Tap on the tile to right option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_RIGHT(2013); override fun getId(): Int = mId } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 3e3dd74ae4b4..c479ab382acb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -788,31 +788,6 @@ class DesktopTasksController( transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) } - /** Moves a task in/out of full immersive state within the desktop. */ - fun toggleDesktopTaskFullImmersiveState(taskInfo: RunningTaskInfo) { - if (taskRepository.isTaskInFullImmersiveState(taskInfo.taskId)) { - exitDesktopTaskFromFullImmersive( - taskInfo, - DesktopImmersiveController.ExitReason.USER_INTERACTION, - ) - } else { - moveDesktopTaskToFullImmersive(taskInfo) - } - } - - private fun moveDesktopTaskToFullImmersive(taskInfo: RunningTaskInfo) { - check(taskInfo.isFreeform) { "Task must already be in freeform" } - desktopImmersiveController.moveTaskToImmersive(taskInfo) - } - - private fun exitDesktopTaskFromFullImmersive( - taskInfo: RunningTaskInfo, - reason: DesktopImmersiveController.ExitReason, - ) { - check(taskInfo.isFreeform) { "Task must already be in freeform" } - desktopImmersiveController.moveTaskToNonImmersive(taskInfo, reason) - } - /** * Quick-resizes a desktop task, toggling between a fullscreen state (represented by the stable * bounds) and a free floating state (either the last saved bounds if available or the default @@ -822,6 +797,8 @@ class DesktopTasksController( taskInfo: RunningTaskInfo, resizeTrigger: ResizeTrigger, inputMethod: InputMethod, + maximizeCujRecorder: (() -> Unit)? = null, + unmaximizeCujRecorder: (() -> Unit)? = null, ) { val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds desktopModeEventLogger.logTaskResizingStarted( @@ -843,6 +820,7 @@ class DesktopTasksController( // helpful to eliminate the current task from logic to calculate taskbar corner rounding. val willMaximize = !isMaximized if (isMaximized) { + unmaximizeCujRecorder?.invoke() // The desktop task is at the maximized width and/or height of the stable bounds. // If the task's pre-maximize stable bounds were saved, toggle the task to those bounds. // Otherwise, toggle to the default bounds. @@ -858,6 +836,7 @@ class DesktopTasksController( } } } else { + maximizeCujRecorder?.invoke() // Save current bounds so that task can be restored back to original bounds if necessary // and toggle to the stable bounds. desktopTilingDecorViewModel.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId) @@ -914,7 +893,7 @@ class DesktopTasksController( toggleDesktopTaskSize( taskInfo, ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, - DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent) + DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent), ) } @@ -2375,7 +2354,7 @@ class DesktopTasksController( if (inImmersive && !requestingImmersive && !RecentsTransitionStateListener.isRunning(recentsTransitionState)) { // Exit immersive if the app is no longer requesting it. - exitDesktopTaskFromFullImmersive( + desktopImmersiveController.moveTaskToNonImmersive( taskInfo, DesktopImmersiveController.ExitReason.APP_NOT_IMMERSIVE ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt index 941115083717..6df3302f47a5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt @@ -119,6 +119,7 @@ class ToggleResizeDesktopTaskTransitionHandler( initialBounds = null boundsAnimator = null interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) + interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW) interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE) } ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt index de9c79ab34fd..c5fca028a1a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt @@ -36,8 +36,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.windowdecor.common.DecorThemeUtil import com.android.wm.shell.windowdecor.common.Theme import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController -import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme +import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainCoroutineDispatcher @@ -74,293 +74,330 @@ class AppHandleEducationController( @ShellMainThread private val applicationCoroutineScope: CoroutineScope, @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher, ) { - private val decorThemeUtil = DecorThemeUtil(context) - private lateinit var openHandleMenuCallback: (Int) -> Unit - private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit + private val decorThemeUtil = DecorThemeUtil(context) + private lateinit var openHandleMenuCallback: (Int) -> Unit + private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit + + init { + runIfEducationFeatureEnabled { + applicationCoroutineScope.launch { + // Central block handling the app handle's educational flow end-to-end. + isAppHandleHintViewedFlow() + .flatMapLatest { isAppHandleHintViewed -> + if (isAppHandleHintViewed) { + // If the education is viewed then return emptyFlow() that completes + // immediately. + // This will help us to not listen to [captionHandleStateFlow] after the + // education + // has been viewed already. + emptyFlow() + } else { + // Listen for changes to window decor's caption handle. + windowDecorCaptionHandleRepository.captionStateFlow + // Wait for few seconds before emitting the latest state. + .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) + .filter { captionState -> + captionState is CaptionState.AppHandle && + appHandleEducationFilter.shouldShowAppHandleEducation( + captionState + ) + } + } + } + .flowOn(backgroundDispatcher) + .collectLatest { captionState -> + val tooltipColorScheme = tooltipColorScheme(captionState) + + showEducation(captionState, tooltipColorScheme) + // After showing first tooltip, mark education as viewed + appHandleEducationDatastoreRepository + .updateAppHandleHintViewedTimestampMillis(true) + } + } - init { - runIfEducationFeatureEnabled { - applicationCoroutineScope.launch { - // Central block handling the app handle's educational flow end-to-end. - isAppHandleHintViewedFlow() - .flatMapLatest { isAppHandleHintViewed -> - if (isAppHandleHintViewed) { - // If the education is viewed then return emptyFlow() that completes immediately. - // This will help us to not listen to [captionHandleStateFlow] after the education - // has been viewed already. - emptyFlow() - } else { - // Listen for changes to window decor's caption handle. + applicationCoroutineScope.launch { + if (isAppHandleHintUsed()) return@launch windowDecorCaptionHandleRepository.captionStateFlow - // Wait for few seconds before emitting the latest state. - .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) .filter { captionState -> - captionState is CaptionState.AppHandle && - appHandleEducationFilter.shouldShowAppHandleEducation(captionState) + captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded } - } + .take(1) + .flowOn(backgroundDispatcher) + .collect { + // If user expands app handle, mark user has used the app handle hint + appHandleEducationDatastoreRepository + .updateAppHandleHintUsedTimestampMillis(true) + } + } + } + } + + private inline fun runIfEducationFeatureEnabled(block: () -> Unit) { + if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) + block() + } + + private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) { + val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds + val tooltipGlobalCoordinates = + Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) + // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the + // cue. + // Populate information important to inflate app handle education tooltip. + val appHandleTooltipConfig = + TooltipEducationViewConfig( + tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, + tooltipColorScheme = tooltipColorScheme, + tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, + tooltipText = getString(R.string.windowing_app_handle_education_tooltip), + arrowDirection = + DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, + onEducationClickAction = { + launchWithExceptionHandling { + showWindowingImageButtonTooltip(tooltipColorScheme) + } + openHandleMenuCallback(captionState.runningTaskInfo.taskId) + }, + onDismissAction = { + launchWithExceptionHandling { + showWindowingImageButtonTooltip(tooltipColorScheme) + } + }, + ) + + windowingEducationViewController.showEducationTooltip( + tooltipViewConfig = appHandleTooltipConfig, + taskId = captionState.runningTaskInfo.taskId, + ) + } + + /** Show tooltip that points to windowing image button in app handle menu */ + private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) { + val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) + val windowingOptionPillHeight = + getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) + val appHandleMenuWidth = + getSize(R.dimen.desktop_mode_handle_menu_width) + + getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) + val appHandleMenuMargins = + getSize(R.dimen.desktop_mode_handle_menu_margin_top) + + getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) + + windowDecorCaptionHandleRepository.captionStateFlow + // After the first tooltip was dismissed, wait for 400 ms and see if the app handle menu + // has been expanded. + .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) + .catchTimeoutAndLog { + // TODO: b/341320146 - Log previous tooltip was dismissed + } + // Wait for few milliseconds before emitting the latest state. + .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) + .filter { captionState -> + // Filter out states when app handle is not visible or not expanded. + captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded } + // Before showing this tooltip, stop listening to further emissions to avoid + // accidentally + // showing the same tooltip on future emissions. + .take(1) .flowOn(backgroundDispatcher) .collectLatest { captionState -> - val tooltipColorScheme = tooltipColorScheme(captionState) + captionState as CaptionState.AppHandle + val appHandleBounds = captionState.globalAppHandleBounds + val tooltipGlobalCoordinates = + Point( + appHandleBounds.left + appHandleBounds.width() / 2 + appHandleMenuWidth / 2, + appHandleBounds.top + + appHandleMenuMargins + + appInfoPillHeight + + windowingOptionPillHeight / 2, + ) + // Populate information important to inflate windowing image button education + // tooltip. + val windowingImageButtonTooltipConfig = + TooltipEducationViewConfig( + tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, + tooltipColorScheme = tooltipColorScheme, + tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, + tooltipText = + getString( + R.string.windowing_desktop_mode_image_button_education_tooltip + ), + arrowDirection = + DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, + onEducationClickAction = { + launchWithExceptionHandling { + showExitWindowingTooltip(tooltipColorScheme) + } + toDesktopModeCallback( + captionState.runningTaskInfo.taskId, + DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON, + ) + }, + onDismissAction = { + launchWithExceptionHandling { + showExitWindowingTooltip(tooltipColorScheme) + } + }, + ) - showEducation(captionState, tooltipColorScheme) - // After showing first tooltip, mark education as viewed - appHandleEducationDatastoreRepository.updateAppHandleHintViewedTimestampMillis(true) + windowingEducationViewController.showEducationTooltip( + taskId = captionState.runningTaskInfo.taskId, + tooltipViewConfig = windowingImageButtonTooltipConfig, + ) } - } + } - applicationCoroutineScope.launch { - if (isAppHandleHintUsed()) return@launch + /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ + private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) { windowDecorCaptionHandleRepository.captionStateFlow + // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered + // desktop mode. + .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) + .catchTimeoutAndLog { + // TODO: b/341320146 - Log previous tooltip was dismissed + } + // Wait for few milliseconds before emitting the latest state. + .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) .filter { captionState -> - captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded + // Filter out states when app header is not visible or expanded. + captionState is CaptionState.AppHeader && !captionState.isHeaderMenuExpanded } + // Before showing this tooltip, stop listening to further emissions to avoid + // accidentally + // showing the same tooltip on future emissions. .take(1) .flowOn(backgroundDispatcher) - .collect { - // If user expands app handle, mark user has used the app handle hint - appHandleEducationDatastoreRepository.updateAppHandleHintUsedTimestampMillis(true) + .collectLatest { captionState -> + captionState as CaptionState.AppHeader + val globalAppChipBounds = captionState.globalAppChipBounds + val tooltipGlobalCoordinates = + Point( + globalAppChipBounds.right, + globalAppChipBounds.top + globalAppChipBounds.height() / 2, + ) + // Populate information important to inflate exit desktop mode education tooltip. + val exitWindowingTooltipConfig = + TooltipEducationViewConfig( + tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, + tooltipColorScheme = tooltipColorScheme, + tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, + tooltipText = + getString(R.string.windowing_desktop_mode_exit_education_tooltip), + arrowDirection = + DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, + onDismissAction = {}, + onEducationClickAction = { + openHandleMenuCallback(captionState.runningTaskInfo.taskId) + }, + ) + windowingEducationViewController.showEducationTooltip( + taskId = captionState.runningTaskInfo.taskId, + tooltipViewConfig = exitWindowingTooltipConfig, + ) } - } } - } - - private inline fun runIfEducationFeatureEnabled(block: () -> Unit) { - if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) block() - } - - private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) { - val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds - val tooltipGlobalCoordinates = - Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) - // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the cue. - // Populate information important to inflate app handle education tooltip. - val appHandleTooltipConfig = - TooltipEducationViewConfig( - tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, - tooltipColorScheme = tooltipColorScheme, - tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, - tooltipText = getString(R.string.windowing_app_handle_education_tooltip), - arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, - onEducationClickAction = { - launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } - openHandleMenuCallback(captionState.runningTaskInfo.taskId) - }, - onDismissAction = { - launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } - }, - ) - - windowingEducationViewController.showEducationTooltip( - tooltipViewConfig = appHandleTooltipConfig, taskId = captionState.runningTaskInfo.taskId) - } - - /** Show tooltip that points to windowing image button in app handle menu */ - private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) { - val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) - val windowingOptionPillHeight = getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) - val appHandleMenuWidth = - getSize(R.dimen.desktop_mode_handle_menu_width) + - getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) - val appHandleMenuMargins = - getSize(R.dimen.desktop_mode_handle_menu_margin_top) + - getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) - - windowDecorCaptionHandleRepository.captionStateFlow - // After the first tooltip was dismissed, wait for 400 ms and see if the app handle menu - // has been expanded. - .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) - .catchTimeoutAndLog { - // TODO: b/341320146 - Log previous tooltip was dismissed - } - // Wait for few milliseconds before emitting the latest state. - .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) - .filter { captionState -> - // Filter out states when app handle is not visible or not expanded. - captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded - } - // Before showing this tooltip, stop listening to further emissions to avoid accidentally - // showing the same tooltip on future emissions. - .take(1) - .flowOn(backgroundDispatcher) - .collectLatest { captionState -> - captionState as CaptionState.AppHandle - val appHandleBounds = captionState.globalAppHandleBounds - val tooltipGlobalCoordinates = - Point( - appHandleBounds.left + appHandleBounds.width() / 2 + appHandleMenuWidth / 2, - appHandleBounds.top + - appHandleMenuMargins + - appInfoPillHeight + - windowingOptionPillHeight / 2) - // Populate information important to inflate windowing image button education tooltip. - val windowingImageButtonTooltipConfig = - TooltipEducationViewConfig( - tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, - tooltipColorScheme = tooltipColorScheme, - tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, - tooltipText = - getString(R.string.windowing_desktop_mode_image_button_education_tooltip), - arrowDirection = - DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, - onEducationClickAction = { - launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } - toDesktopModeCallback( - captionState.runningTaskInfo.taskId, - DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) - }, - onDismissAction = { - launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } - }, - ) - windowingEducationViewController.showEducationTooltip( - taskId = captionState.runningTaskInfo.taskId, - tooltipViewConfig = windowingImageButtonTooltipConfig) - } - } + private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme { + context.withStyledAttributes( + set = null, + attrs = + intArrayOf( + com.android.internal.R.attr.materialColorOnTertiaryFixed, + com.android.internal.R.attr.materialColorTertiaryFixed, + com.android.internal.R.attr.materialColorTertiaryFixedDim, + ), + defStyleAttr = 0, + defStyleRes = 0, + ) { + val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0) + val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0) + val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0) + val taskInfo = (captionState as CaptionState.AppHandle).runningTaskInfo - /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ - private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) { - windowDecorCaptionHandleRepository.captionStateFlow - // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered - // desktop mode. - .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) - .catchTimeoutAndLog { - // TODO: b/341320146 - Log previous tooltip was dismissed - } - // Wait for few milliseconds before emitting the latest state. - .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) - .filter { captionState -> - // Filter out states when app header is not visible or expanded. - captionState is CaptionState.AppHeader && !captionState.isHeaderMenuExpanded - } - // Before showing this tooltip, stop listening to further emissions to avoid accidentally - // showing the same tooltip on future emissions. - .take(1) - .flowOn(backgroundDispatcher) - .collectLatest { captionState -> - captionState as CaptionState.AppHeader - val globalAppChipBounds = captionState.globalAppChipBounds - val tooltipGlobalCoordinates = - Point( - globalAppChipBounds.right, - globalAppChipBounds.top + globalAppChipBounds.height() / 2) - // Populate information important to inflate exit desktop mode education tooltip. - val exitWindowingTooltipConfig = - TooltipEducationViewConfig( - tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, - tooltipColorScheme = tooltipColorScheme, - tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, - tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip), - arrowDirection = - DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, - onDismissAction = {}, - onEducationClickAction = { - openHandleMenuCallback(captionState.runningTaskInfo.taskId) - }, - ) - windowingEducationViewController.showEducationTooltip( - taskId = captionState.runningTaskInfo.taskId, - tooltipViewConfig = exitWindowingTooltipConfig, - ) + val tooltipContainerColor = + if (decorThemeUtil.getAppTheme(taskInfo) == Theme.LIGHT) { + tertiaryFixed + } else { + tertiaryFixedDim + } + return TooltipColorScheme(tooltipContainerColor, onTertiaryFixed, onTertiaryFixed) } - } + return TooltipColorScheme(0, 0, 0) + } - private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme { - context.withStyledAttributes( - set = null, - attrs = - intArrayOf( - com.android.internal.R.attr.materialColorOnTertiaryFixed, - com.android.internal.R.attr.materialColorTertiaryFixed, - com.android.internal.R.attr.materialColorTertiaryFixedDim), - defStyleAttr = 0, - defStyleRes = 0) { - val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0) - val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0) - val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0) - val taskInfo = (captionState as CaptionState.AppHandle).runningTaskInfo + /** + * Setup callbacks for app handle education tooltips. + * + * @param openHandleMenuCallback callback invoked to open app handle menu or app chip menu. + * @param toDesktopModeCallback callback invoked to move task into desktop mode. + */ + fun setAppHandleEducationTooltipCallbacks( + openHandleMenuCallback: (taskId: Int) -> Unit, + toDesktopModeCallback: (taskId: Int, DesktopModeTransitionSource) -> Unit, + ) { + this.openHandleMenuCallback = openHandleMenuCallback + this.toDesktopModeCallback = toDesktopModeCallback + } - val tooltipContainerColor = - if (decorThemeUtil.getAppTheme(taskInfo) == Theme.LIGHT) { - tertiaryFixed - } else { - tertiaryFixedDim - } - return TooltipColorScheme(tooltipContainerColor, onTertiaryFixed, onTertiaryFixed) + private inline fun <T> Flow<T>.catchTimeoutAndLog(crossinline block: () -> Unit) = + catch { exception -> + if (exception is TimeoutCancellationException) block() else throw exception } - return TooltipColorScheme(0, 0, 0) - } - /** - * Setup callbacks for app handle education tooltips. - * - * @param openHandleMenuCallback callback invoked to open app handle menu or app chip menu. - * @param toDesktopModeCallback callback invoked to move task into desktop mode. - */ - fun setAppHandleEducationTooltipCallbacks( - openHandleMenuCallback: (taskId: Int) -> Unit, - toDesktopModeCallback: (taskId: Int, DesktopModeTransitionSource) -> Unit - ) { - this.openHandleMenuCallback = openHandleMenuCallback - this.toDesktopModeCallback = toDesktopModeCallback - } - - private inline fun <T> Flow<T>.catchTimeoutAndLog(crossinline block: () -> Unit) = - catch { exception -> - if (exception is TimeoutCancellationException) block() else throw exception - } - - private fun launchWithExceptionHandling(block: suspend () -> Unit) = - applicationCoroutineScope.launch { - try { - block() - } catch (e: Throwable) { - Slog.e(TAG, "Error: ", e) + private fun launchWithExceptionHandling(block: suspend () -> Unit) = + applicationCoroutineScope.launch { + try { + block() + } catch (e: Throwable) { + Slog.e(TAG, "Error: ", e) + } } - } - /** - * Listens to the changes to [WindowingEducationProto#hasAppHandleHintViewedTimestampMillis()] in - * datastore proto object. - * - * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That means - * it will always emit app handle hint has not been viewed yet. - */ - private fun isAppHandleHintViewedFlow(): Flow<Boolean> = - appHandleEducationDatastoreRepository.dataStoreFlow - .map { preferences -> - preferences.hasAppHandleHintViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS - } - .distinctUntilChanged() + /** + * Listens to the changes to [WindowingEducationProto#hasAppHandleHintViewedTimestampMillis()] + * in datastore proto object. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That + * means it will always emit app handle hint has not been viewed yet. + */ + private fun isAppHandleHintViewedFlow(): Flow<Boolean> = + appHandleEducationDatastoreRepository.dataStoreFlow + .map { preferences -> + preferences.hasAppHandleHintViewedTimestampMillis() && + !SHOULD_OVERRIDE_EDUCATION_CONDITIONS + } + .distinctUntilChanged() - /** - * Listens to the changes to [WindowingEducationProto#hasAppHandleHintUsedTimestampMillis()] in - * datastore proto object. - */ - private suspend fun isAppHandleHintUsed(): Boolean = - appHandleEducationDatastoreRepository.dataStoreFlow.first().hasAppHandleHintUsedTimestampMillis() + /** + * Listens to the changes to [WindowingEducationProto#hasAppHandleHintUsedTimestampMillis()] in + * datastore proto object. + */ + private suspend fun isAppHandleHintUsed(): Boolean = + appHandleEducationDatastoreRepository.dataStoreFlow + .first() + .hasAppHandleHintUsedTimestampMillis() - private fun getSize(@DimenRes resourceId: Int): Int { - if (resourceId == Resources.ID_NULL) return 0 - return context.resources.getDimensionPixelSize(resourceId) - } + private fun getSize(@DimenRes resourceId: Int): Int { + if (resourceId == Resources.ID_NULL) return 0 + return context.resources.getDimensionPixelSize(resourceId) + } - private fun getString(@StringRes resId: Int): String = context.resources.getString(resId) + private fun getString(@StringRes resId: Int): String = context.resources.getString(resId) - companion object { - const val TAG = "AppHandleEducationController" - val APP_HANDLE_EDUCATION_DELAY_MILLIS: Long - get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L) + companion object { + const val TAG = "AppHandleEducationController" + val APP_HANDLE_EDUCATION_DELAY_MILLIS: Long + get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L) - val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long - get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L) + val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long + get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L) - val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean - get() = - SystemProperties.getBoolean( - "persist.desktop_windowing_app_handle_education_override_conditions", false) - } + val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean + get() = + SystemProperties.getBoolean( + "persist.desktop_windowing_app_handle_education_override_conditions", + false, + ) + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt index 7a7829334fb6..9990846fc92e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt @@ -32,106 +32,114 @@ import java.time.Duration /** Filters incoming app handle education triggers based on set conditions. */ class AppHandleEducationFilter( private val context: Context, - private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository + private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository, ) { - private val usageStatsManager = - context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - - /** - * Returns true if conditions to show app handle education are met, returns false otherwise. - * - * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return - * ![captionState.isHandleMenuExpanded]. - */ - suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean { - if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false - if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true - - val focusAppPackageName = - captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false - val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto() - - return isFocusAppInAllowlist(focusAppPackageName) && - !isOtherEducationShowing() && - hasSufficientTimeSinceSetup() && - !isAppHandleHintViewedBefore(windowingEducationProto) && - !isAppHandleHintUsedBefore(windowingEducationProto) && - hasMinAppUsage(windowingEducationProto, focusAppPackageName) - } - - private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean = - focusAppPackageName in - context.resources.getStringArray( - R.array.desktop_windowing_app_handle_education_allowlist_apps) - - // TODO: b/350953004 - Add checks based on App compat - // TODO: b/350951797 - Add checks based on PKT tips education - private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() - - private fun isTaskbarEducationShowing(): Boolean = - Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1 - - private fun hasSufficientTimeSinceSetup(): Boolean = - Duration.ofMillis(SystemClock.elapsedRealtime()) > - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_required_time_since_setup_seconds) - - private fun isAppHandleHintViewedBefore(windowingEducationProto: WindowingEducationProto): Boolean = - windowingEducationProto.hasAppHandleHintViewedTimestampMillis() - - private fun isAppHandleHintUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean = - windowingEducationProto.hasAppHandleHintUsedTimestampMillis() - - private suspend fun hasMinAppUsage( - windowingEducationProto: WindowingEducationProto, - focusAppPackageName: String - ): Boolean = - (launchCountByPackageName(windowingEducationProto)[focusAppPackageName] ?: 0) >= - context.resources.getInteger(R.integer.desktop_windowing_education_min_app_launch_count) - - private suspend fun launchCountByPackageName( - windowingEducationProto: WindowingEducationProto - ): Map<String, Int> = - if (isAppUsageCacheStale(windowingEducationProto)) { - // Query and return user stats, update cache in datastore - getAndCacheAppUsageStats() - } else { - // Return cached usage stats - windowingEducationProto.appHandleEducation.appUsageStatsMap - } - - private fun isAppUsageCacheStale(windowingEducationProto: WindowingEducationProto): Boolean { - val currentTime = currentTimeInDuration() - val lastUpdateTime = - Duration.ofMillis( - windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis) - val appUsageStatsCachingInterval = - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_app_usage_cache_interval_seconds) - return (currentTime - lastUpdateTime) > appUsageStatsCachingInterval - } - - private suspend fun getAndCacheAppUsageStats(): Map<String, Int> { - val currentTime = currentTimeInDuration() - val appUsageStats = queryAppUsageStats() - appHandleEducationDatastoreRepository.updateAppUsageStats(appUsageStats, currentTime) - return appUsageStats - } - - private fun queryAppUsageStats(): Map<String, Int> { - val endTime = currentTimeInDuration() - val appLaunchInterval = - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_app_launch_interval_seconds) - val startTime = endTime - appLaunchInterval - - return usageStatsManager - .queryAndAggregateUsageStats(startTime.toMillis(), endTime.toMillis()) - .mapValues { it.value.appLaunchCount } - } - - private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration = - Duration.ofSeconds(context.resources.getInteger(resourceId).toLong()) - - private fun currentTimeInDuration(): Duration = Duration.ofMillis(System.currentTimeMillis()) + private val usageStatsManager = + context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager + + /** + * Returns true if conditions to show app handle education are met, returns false otherwise. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return + * ![captionState.isHandleMenuExpanded]. + */ + suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean { + if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false + if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true + + val focusAppPackageName = + captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false + val windowingEducationProto = + appHandleEducationDatastoreRepository.windowingEducationProto() + + return isFocusAppInAllowlist(focusAppPackageName) && + !isOtherEducationShowing() && + hasSufficientTimeSinceSetup() && + !isAppHandleHintViewedBefore(windowingEducationProto) && + !isAppHandleHintUsedBefore(windowingEducationProto) && + hasMinAppUsage(windowingEducationProto, focusAppPackageName) + } + + private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean = + focusAppPackageName in + context.resources.getStringArray( + R.array.desktop_windowing_app_handle_education_allowlist_apps + ) + + // TODO: b/350953004 - Add checks based on App compat + // TODO: b/350951797 - Add checks based on PKT tips education + private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() + + private fun isTaskbarEducationShowing(): Boolean = + Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1 + + private fun hasSufficientTimeSinceSetup(): Boolean = + Duration.ofMillis(SystemClock.elapsedRealtime()) > + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_required_time_since_setup_seconds + ) + + private fun isAppHandleHintViewedBefore( + windowingEducationProto: WindowingEducationProto + ): Boolean = windowingEducationProto.hasAppHandleHintViewedTimestampMillis() + + private fun isAppHandleHintUsedBefore( + windowingEducationProto: WindowingEducationProto + ): Boolean = windowingEducationProto.hasAppHandleHintUsedTimestampMillis() + + private suspend fun hasMinAppUsage( + windowingEducationProto: WindowingEducationProto, + focusAppPackageName: String, + ): Boolean = + (launchCountByPackageName(windowingEducationProto)[focusAppPackageName] ?: 0) >= + context.resources.getInteger(R.integer.desktop_windowing_education_min_app_launch_count) + + private suspend fun launchCountByPackageName( + windowingEducationProto: WindowingEducationProto + ): Map<String, Int> = + if (isAppUsageCacheStale(windowingEducationProto)) { + // Query and return user stats, update cache in datastore + getAndCacheAppUsageStats() + } else { + // Return cached usage stats + windowingEducationProto.appHandleEducation.appUsageStatsMap + } + + private fun isAppUsageCacheStale(windowingEducationProto: WindowingEducationProto): Boolean { + val currentTime = currentTimeInDuration() + val lastUpdateTime = + Duration.ofMillis( + windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis + ) + val appUsageStatsCachingInterval = + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_app_usage_cache_interval_seconds + ) + return (currentTime - lastUpdateTime) > appUsageStatsCachingInterval + } + + private suspend fun getAndCacheAppUsageStats(): Map<String, Int> { + val currentTime = currentTimeInDuration() + val appUsageStats = queryAppUsageStats() + appHandleEducationDatastoreRepository.updateAppUsageStats(appUsageStats, currentTime) + return appUsageStats + } + + private fun queryAppUsageStats(): Map<String, Int> { + val endTime = currentTimeInDuration() + val appLaunchInterval = + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_app_launch_interval_seconds + ) + val startTime = endTime - appLaunchInterval + + return usageStatsManager + .queryAndAggregateUsageStats(startTime.toMillis(), endTime.toMillis()) + .mapValues { it.value.appLaunchCount } + } + + private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration = + Duration.ofSeconds(context.resources.getInteger(resourceId).toLong()) + + private fun currentTimeInDuration(): Duration = Duration.ofMillis(System.currentTimeMillis()) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt index 693da81ec4a0..bfe1b12c9605 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt @@ -53,8 +53,8 @@ import kotlinx.coroutines.launch /** * Controls App-to-Web education end to end. * - * Listen to usages of App-to-Web, calls an api to check if the education - * should be shown and controls education UI. + * Listen to usages of App-to-Web, calls an api to check if the education should be shown and + * controls education UI. */ @OptIn(kotlinx.coroutines.FlowPreview::class) @kotlinx.coroutines.ExperimentalCoroutinesApi @@ -88,8 +88,9 @@ class AppToWebEducationController( .debounce(APP_TO_WEB_EDUCATION_DELAY_MILLIS) .filter { captionState -> captionState !is CaptionState.NoCaption && - appToWebEducationFilter - .shouldShowAppToWebEducation(captionState) + appToWebEducationFilter.shouldShowAppToWebEducation( + captionState + ) } } } @@ -104,12 +105,12 @@ class AppToWebEducationController( applicationCoroutineScope.launch { if (isFeatureUsed()) return@launch - windowDecorCaptionHandleRepository.appToWebUsageFlow - .collect { - // If user utilizes App-to-Web, mark user has used the feature - appToWebEducationDatastoreRepository - .updateFeatureUsedTimestampMillis(isViewed = true) - } + windowDecorCaptionHandleRepository.appToWebUsageFlow.collect { + // If user utilizes App-to-Web, mark user has used the feature + appToWebEducationDatastoreRepository.updateFeatureUsedTimestampMillis( + isViewed = true + ) + } } } } @@ -126,10 +127,8 @@ class AppToWebEducationController( val appHandleBounds = captionState.globalAppHandleBounds val educationWidth = loadDimensionPixelSize(R.dimen.desktop_windowing_education_promo_width) - educationGlobalCoordinates = Point( - appHandleBounds.centerX() - educationWidth / 2, - appHandleBounds.bottom - ) + educationGlobalCoordinates = + Point(appHandleBounds.centerX() - educationWidth / 2, appHandleBounds.bottom) taskId = captionState.runningTaskInfo.taskId } @@ -152,19 +151,22 @@ class AppToWebEducationController( viewGlobalCoordinates = educationGlobalCoordinates, educationText = getString(R.string.desktop_windowing_app_to_web_education_text), widthId = R.dimen.desktop_windowing_education_promo_width, - heightId = R.dimen.desktop_windowing_education_promo_height + heightId = R.dimen.desktop_windowing_education_promo_height, ) windowingEducationViewController.showEducation( - viewConfig = educationConfig, taskId = taskId) + viewConfig = educationConfig, + taskId = taskId, + ) } private fun educationColorScheme(captionState: CaptionState): EducationColorScheme? { - val taskInfo: RunningTaskInfo = when (captionState) { - is CaptionState.AppHandle -> captionState.runningTaskInfo - is CaptionState.AppHeader -> captionState.runningTaskInfo - else -> return null - } + val taskInfo: RunningTaskInfo = + when (captionState) { + is CaptionState.AppHandle -> captionState.runningTaskInfo + is CaptionState.AppHeader -> captionState.runningTaskInfo + else -> return null + } val colorScheme = decorThemeUtil.getColorScheme(taskInfo) val tooltipContainerColor = colorScheme.surfaceBright.toArgb() @@ -178,8 +180,7 @@ class AppToWebEducationController( */ private fun isEducationViewLimitReachedFlow(): Flow<Boolean> = appToWebEducationDatastoreRepository.dataStoreFlow - .map { preferences -> - appToWebEducationFilter.isEducationViewLimitReached(preferences)} + .map { preferences -> appToWebEducationFilter.isEducationViewLimitReached(preferences) } .distinctUntilChanged() /** @@ -199,9 +200,6 @@ class AppToWebEducationController( companion object { const val TAG = "AppToWebEducationController" val APP_TO_WEB_EDUCATION_DELAY_MILLIS: Long - get() = SystemProperties.getLong( - "persist.windowing_app_handle_education_delay", - 3000L - ) + get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt index feee6ed86da1..e272b54dd907 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt @@ -30,39 +30,41 @@ import java.time.Duration /** Filters incoming App-to-Web education triggers based on set conditions. */ class AppToWebEducationFilter( private val context: Context, - private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository + private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository, ) { /** Returns true if conditions to show App-to-web education are met, returns false otherwise. */ suspend fun shouldShowAppToWebEducation(captionState: CaptionState): Boolean { - val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) = when (captionState) { - is CaptionState.AppHandle -> - Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) - is CaptionState.AppHeader -> - Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) - else -> return false - } + val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) = + when (captionState) { + is CaptionState.AppHandle -> + Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) + is CaptionState.AppHeader -> + Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) + else -> return false + } val focusAppPackageName = taskInfo.topActivityInfo?.packageName ?: return false val windowingEducationProto = appToWebEducationDatastoreRepository.windowingEducationProto() return !isOtherEducationShowing() && - !isEducationViewLimitReached(windowingEducationProto) && - hasSufficientTimeSinceSetup() && - !isFeatureUsedBefore(windowingEducationProto) && - isCapturedLinkAvailable && - isFocusAppInAllowlist(focusAppPackageName) + !isEducationViewLimitReached(windowingEducationProto) && + hasSufficientTimeSinceSetup() && + !isFeatureUsedBefore(windowingEducationProto) && + isCapturedLinkAvailable && + isFocusAppInAllowlist(focusAppPackageName) } private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean = focusAppPackageName in - context.resources.getStringArray( - R.array.desktop_windowing_app_to_web_education_allowlist_apps) + context.resources.getStringArray( + R.array.desktop_windowing_app_to_web_education_allowlist_apps + ) // TODO: b/350953004 - Add checks based on App compat // TODO: b/350951797 - Add checks based on PKT tips education - private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() || - isCompatUiEducationShowing() + private fun isOtherEducationShowing(): Boolean = + isTaskbarEducationShowing() || isCompatUiEducationShowing() private fun isTaskbarEducationShowing(): Boolean = Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1 @@ -72,13 +74,14 @@ class AppToWebEducationFilter( private fun hasSufficientTimeSinceSetup(): Boolean = Duration.ofMillis(SystemClock.elapsedRealtime()) > - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_required_time_since_setup_seconds) + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_required_time_since_setup_seconds + ) /** Returns true if education is viewed maximum amount of times it should be shown. */ fun isEducationViewLimitReached(windowingEducationProto: WindowingEducationProto): Boolean = windowingEducationProto.getAppToWebEducation().getEducationShownCount() >= - MAXIMUM_TIMES_EDUCATION_SHOWN + MAXIMUM_TIMES_EDUCATION_SHOWN private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean = windowingEducationProto.hasFeatureUsedTimestampMillis() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt index 5e0c0007e2eb..3e120b09a0b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt @@ -42,103 +42,109 @@ import kotlinx.coroutines.flow.first class AppHandleEducationDatastoreRepository @VisibleForTesting constructor(private val dataStore: DataStore<WindowingEducationProto>) { - constructor( - context: Context - ) : this( - DataStoreFactory.create( - serializer = WindowingEducationProtoSerializer, - produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) })) + constructor( + context: Context + ) : this( + DataStoreFactory.create( + serializer = WindowingEducationProtoSerializer, + produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) }, + ) + ) - /** Provides dataStore.data flow and handles exceptions thrown during collection */ - val dataStoreFlow: Flow<WindowingEducationProto> = - dataStore.data.catch { exception -> - // dataStore.data throws an IOException when an error is encountered when reading data - if (exception is IOException) { - Log.e( - TAG, - "Error in reading app handle education related data from datastore, data is " + - "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH", - exception) - } else { - throw exception + /** Provides dataStore.data flow and handles exceptions thrown during collection */ + val dataStoreFlow: Flow<WindowingEducationProto> = + dataStore.data.catch { exception -> + // dataStore.data throws an IOException when an error is encountered when reading data + if (exception is IOException) { + Log.e( + TAG, + "Error in reading app handle education related data from datastore, data is " + + "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH", + exception, + ) + } else { + throw exception + } } - } - /** - * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the - * DataStore is empty or there's an error reading, it returns the default value of Proto. - */ - suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first() + /** + * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the + * DataStore is empty or there's an error reading, it returns the default value of Proto. + */ + suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first() - /** - * Updates [WindowingEducationProto.appHandleHintViewedTimestampMillis_] field - * in datastore with current timestamp if [isViewed] is true, if not then - * clears the field. - */ - suspend fun updateAppHandleHintViewedTimestampMillis(isViewed: Boolean) { - dataStore.updateData { preferences -> - if (isViewed) { - preferences - .toBuilder() - .setAppHandleHintViewedTimestampMillis(System.currentTimeMillis()) - .build() - } else { - preferences.toBuilder().clearAppHandleHintViewedTimestampMillis().build() - } + /** + * Updates [WindowingEducationProto.appHandleHintViewedTimestampMillis_] field in datastore with + * current timestamp if [isViewed] is true, if not then clears the field. + */ + suspend fun updateAppHandleHintViewedTimestampMillis(isViewed: Boolean) { + dataStore.updateData { preferences -> + if (isViewed) { + preferences + .toBuilder() + .setAppHandleHintViewedTimestampMillis(System.currentTimeMillis()) + .build() + } else { + preferences.toBuilder().clearAppHandleHintViewedTimestampMillis().build() + } + } } - } - /** - * Updates [WindowingEducationProto.appHandleHintUsedTimestampMillis_] field - * in datastore with current timestamp if [isViewed] is true, if not then - * clears the field. - */ - suspend fun updateAppHandleHintUsedTimestampMillis(isViewed: Boolean) { - dataStore.updateData { preferences -> - if (isViewed) { - preferences.toBuilder().setAppHandleHintUsedTimestampMillis(System.currentTimeMillis()).build() - } else { - preferences.toBuilder().clearAppHandleHintUsedTimestampMillis().build() - } + /** + * Updates [WindowingEducationProto.appHandleHintUsedTimestampMillis_] field in datastore with + * current timestamp if [isViewed] is true, if not then clears the field. + */ + suspend fun updateAppHandleHintUsedTimestampMillis(isViewed: Boolean) { + dataStore.updateData { preferences -> + if (isViewed) { + preferences + .toBuilder() + .setAppHandleHintUsedTimestampMillis(System.currentTimeMillis()) + .build() + } else { + preferences.toBuilder().clearAppHandleHintUsedTimestampMillis().build() + } + } } - } - /** - * Updates [AppHandleEducation.appUsageStats] and - * [AppHandleEducation.appUsageStatsLastUpdateTimestampMillis] fields in datastore with - * [appUsageStats] and [appUsageStatsLastUpdateTimestamp]. - */ - suspend fun updateAppUsageStats( - appUsageStats: Map<String, Int>, - appUsageStatsLastUpdateTimestamp: Duration - ) { - val currentAppHandleProto = windowingEducationProto().appHandleEducation.toBuilder() - currentAppHandleProto - .putAllAppUsageStats(appUsageStats) - .setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestamp.toMillis()) - dataStore.updateData { preferences: WindowingEducationProto -> - preferences.toBuilder().setAppHandleEducation(currentAppHandleProto).build() + /** + * Updates [AppHandleEducation.appUsageStats] and + * [AppHandleEducation.appUsageStatsLastUpdateTimestampMillis] fields in datastore with + * [appUsageStats] and [appUsageStatsLastUpdateTimestamp]. + */ + suspend fun updateAppUsageStats( + appUsageStats: Map<String, Int>, + appUsageStatsLastUpdateTimestamp: Duration, + ) { + val currentAppHandleProto = windowingEducationProto().appHandleEducation.toBuilder() + currentAppHandleProto + .putAllAppUsageStats(appUsageStats) + .setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestamp.toMillis()) + dataStore.updateData { preferences: WindowingEducationProto -> + preferences.toBuilder().setAppHandleEducation(currentAppHandleProto).build() + } } - } - companion object { - private const val TAG = "AppHandleEducationDatastoreRepository" - private const val APP_HANDLE_EDUCATION_DATASTORE_FILEPATH = "app_handle_education.pb" + companion object { + private const val TAG = "AppHandleEducationDatastoreRepository" + private const val APP_HANDLE_EDUCATION_DATASTORE_FILEPATH = "app_handle_education.pb" - object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> { + object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> { - override val defaultValue: WindowingEducationProto = - WindowingEducationProto.getDefaultInstance() + override val defaultValue: WindowingEducationProto = + WindowingEducationProto.getDefaultInstance() - override suspend fun readFrom(input: InputStream): WindowingEducationProto = - try { - WindowingEducationProto.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } + override suspend fun readFrom(input: InputStream): WindowingEducationProto = + try { + WindowingEducationProto.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } - override suspend fun writeTo(windowingProto: WindowingEducationProto, output: OutputStream) = - windowingProto.writeTo(output) + override suspend fun writeTo( + windowingProto: WindowingEducationProto, + output: OutputStream, + ) = windowingProto.writeTo(output) + } } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt index 8be6e6dff6fe..e5ad901d1435 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt @@ -25,12 +25,12 @@ import androidx.datastore.core.Serializer import androidx.datastore.dataStoreFile import com.android.framework.protobuf.InvalidProtocolBufferException import com.android.internal.annotations.VisibleForTesting -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.first import java.io.IOException import java.io.InputStream import java.io.OutputStream +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.first /** Updates data in App-to-Web's education datastore. */ class AppToWebEducationDatastoreRepository @@ -41,7 +41,9 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { ) : this( DataStoreFactory.create( serializer = WindowingEducationProtoSerializer, - produceFile = { context.dataStoreFile(APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH) })) + produceFile = { context.dataStoreFile(APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH) }, + ) + ) /** Provides dataStore.data flow and handles exceptions thrown during collection */ val dataStoreFlow: Flow<WindowingEducationProto> = @@ -51,8 +53,10 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { Slog.e( TAG, "Error in reading App-to-Web education related data from datastore," + - "data is stored in a file named" + - "$APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH", exception) + "data is stored in a file named" + + "$APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH", + exception, + ) } else { throw exception } @@ -72,26 +76,26 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { dataStore.updateData { preferences -> if (isViewed) { preferences - .toBuilder().setFeatureUsedTimestampMillis(System.currentTimeMillis()).build() + .toBuilder() + .setFeatureUsedTimestampMillis(System.currentTimeMillis()) + .build() } else { preferences.toBuilder().clearFeatureUsedTimestampMillis().build() } } } - /** - * Increases [AppToWebEducation.educationShownCount] field by one. - */ + /** Increases [AppToWebEducation.educationShownCount] field by one. */ suspend fun updateEducationShownCount() { val currentAppHandleProto = windowingEducationProto().appToWebEducation.toBuilder() - currentAppHandleProto - .setEducationShownCount(currentAppHandleProto.getEducationShownCount() + 1) + currentAppHandleProto.setEducationShownCount( + currentAppHandleProto.getEducationShownCount() + 1 + ) dataStore.updateData { preferences -> preferences.toBuilder().setAppToWebEducation(currentAppHandleProto).build() } } - companion object { private const val TAG = "AppToWebEducationDatastoreRepository" private const val APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH = "app_to_web_education.pb" @@ -110,7 +114,7 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { override suspend fun writeTo( windowingProto: WindowingEducationProto, - output: OutputStream + output: OutputStream, ) = windowingProto.writeTo(output) } } 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 30f1948efa2d..fb4afe41e193 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 @@ -26,6 +26,8 @@ import static android.util.RotationUtils.rotateBounds; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; +import static com.android.wm.shell.desktopmode.DesktopModeUtils.calculateInitialBounds; +import static com.android.wm.shell.desktopmode.DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START; @@ -152,6 +154,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Nullable private final PipPerfHintController mPipPerfHintController; private final Optional<DesktopRepository> mDesktopRepositoryOptional; private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; + private final DisplayController mDisplayController; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellExecutor mMainExecutor; @@ -425,6 +428,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipPerfHintController = pipPerfHintControllerOptional.orElse(null); mDesktopRepositoryOptional = desktopRepositoryOptional; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mDisplayController = displayController; mTaskOrganizer = shellTaskOrganizer; mMainExecutor = mainExecutor; @@ -754,19 +758,30 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** Returns the bounds to restore to when exiting PIP mode. */ // TODO(b/377581840): Instead of manually tracking bounds, use bounds from Core. public Rect getExitDestinationBounds() { - if (isPipLaunchedInDesktopMode()) { - final Rect freeformBounds = mDesktopRepositoryOptional.get().removeBoundsBeforeMinimize( + if (isPipExitingToDesktopMode()) { + // If we are exiting PiP while device is in Desktop mode: + // 1) If PiP was entered via Desktop minimize (e.g. via minimize button), restore to the + // previous freeform bounds that is saved in DesktopRepository. + // 2) If PiP was entered through other means (e.g. user swipe up), exit to initial + // freeform bounds. Note that this case has a flicker at the moment (b/379984108). + Rect freeformBounds = mDesktopRepositoryOptional.get().removeBoundsBeforeMinimize( mTaskInfo.taskId); - return Objects.requireNonNullElseGet(freeformBounds, mPipBoundsState::getDisplayBounds); + return freeformBounds != null + ? freeformBounds + : calculateInitialBounds( + mDisplayController.getDisplayLayout(mTaskInfo.displayId), + mTaskInfo, + DESKTOP_MODE_INITIAL_BOUNDS_SCALE); } return mPipBoundsState.getDisplayBounds(); } - /** Returns whether PiP was launched while in desktop mode. */ - // TODO(377581840): Update this check to include non-minimized cases, e.g. split to PiP etc. - private boolean isPipLaunchedInDesktopMode() { + /** Returns whether PiP is exiting while we're in desktop mode. */ + // TODO(b/377581840): Update this check to include non-minimized cases, e.g. split to PiP etc. + private boolean isPipExitingToDesktopMode() { return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent() - && mDesktopRepositoryOptional.get().isMinimizedTask(mTaskInfo.taskId); + && (mDesktopRepositoryOptional.get().getVisibleTaskCount(mTaskInfo.displayId) > 0 + || isDisplayInFreeform()); } private void exitLaunchIntoPipTask(WindowContainerTransaction wct) { @@ -1827,23 +1842,28 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, == SPLIT_POSITION_TOP_OR_LEFT; } + private boolean isDisplayInFreeform() { + final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( + mTaskInfo.displayId); + if (tdaInfo != null) { + return tdaInfo.configuration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FREEFORM; + } + return false; + } + /** * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined * and can be overridden to restore to an alternate windowing mode. */ public int getOutPipWindowingMode() { - final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( - mTaskInfo.displayId); - - // If PiP was launched while in desktop mode (we should return the task to freeform - // windowing mode): + // If we are exiting PiP while the device is in Desktop mode (the task should expand to + // freeform windowing mode): // 1) If the display windowing mode is freeform, set windowing mode to undefined so it will // resolve the windowing mode to the display's windowing mode. // 2) If the display windowing mode is not freeform, set windowing mode to freeform. - if (tdaInfo != null && isPipLaunchedInDesktopMode()) { - final int displayWindowingMode = - tdaInfo.configuration.windowConfiguration.getWindowingMode(); - if (displayWindowingMode == WINDOWING_MODE_FREEFORM) { + if (isPipExitingToDesktopMode()) { + if (isDisplayInFreeform()) { return WINDOWING_MODE_UNDEFINED; } else { return WINDOWING_MODE_FREEFORM; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index 6d2df952ee58..2c5d346224a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -254,6 +254,7 @@ public class PipController implements ConfigurationChangeListener, @Override public void onConfigurationChanged(Configuration newConfiguration) { mPipDisplayLayoutState.onConfigurationChanged(); + mPipTouchHandler.onConfigurationChanged(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index 5438a014af00..7145e0699765 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip2.phone; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; @@ -25,6 +26,7 @@ import android.content.Context; import android.graphics.Matrix; import android.graphics.Rect; import android.view.SurfaceControl; +import android.window.DisplayAreaInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -33,13 +35,19 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; +import com.android.window.flags.Flags; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import com.android.wm.shell.pip2.animation.PipAlphaAnimator; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import java.util.Objects; +import java.util.Optional; + /** * Scheduler for Shell initiated PiP transitions and animations. */ @@ -50,6 +58,8 @@ public class PipScheduler { private final PipBoundsState mPipBoundsState; private final ShellExecutor mMainExecutor; private final PipTransitionState mPipTransitionState; + private final Optional<DesktopRepository> mDesktopRepositoryOptional; + private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; private PipTransitionController mPipTransitionController; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; @@ -61,11 +71,15 @@ public class PipScheduler { public PipScheduler(Context context, PipBoundsState pipBoundsState, ShellExecutor mainExecutor, - PipTransitionState pipTransitionState) { + PipTransitionState pipTransitionState, + Optional<DesktopRepository> desktopRepositoryOptional, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { mContext = context; mPipBoundsState = pipBoundsState; mMainExecutor = mainExecutor; mPipTransitionState = pipTransitionState; + mDesktopRepositoryOptional = desktopRepositoryOptional; + mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); @@ -87,7 +101,7 @@ public class PipScheduler { wct.setBounds(pipTaskToken, null); // if we are hitting a multi-activity case // windowing mode change will reparent to original host task - wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED); + wct.setWindowingMode(pipTaskToken, getOutPipWindowingMode()); return wct; } @@ -241,6 +255,47 @@ public class PipScheduler { maybeUpdateMovementBounds(); } + /** Returns whether the display is in freeform windowing mode. */ + private boolean isDisplayInFreeform() { + final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( + Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId); + if (tdaInfo != null) { + return tdaInfo.configuration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FREEFORM; + } + return false; + } + + /** Returns whether PiP is exiting while we're in desktop mode. */ + private boolean isPipExitingToDesktopMode() { + return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent() + && (mDesktopRepositoryOptional.get().getVisibleTaskCount( + Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId) > 0 + || isDisplayInFreeform()); + } + + /** + * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined + * and can be overridden to restore to an alternate windowing mode. + */ + private int getOutPipWindowingMode() { + // If we are exiting PiP while the device is in Desktop mode (the task should expand to + // freeform windowing mode): + // 1) If the display windowing mode is freeform, set windowing mode to undefined so it will + // resolve the windowing mode to the display's windowing mode. + // 2) If the display windowing mode is not freeform, set windowing mode to freeform. + if (isPipExitingToDesktopMode()) { + if (isDisplayInFreeform()) { + return WINDOWING_MODE_UNDEFINED; + } else { + return WINDOWING_MODE_FREEFORM; + } + } + + // By default, or if the task is going to fullscreen, reset the windowing mode to undefined. + return WINDOWING_MODE_UNDEFINED; + } + @VisibleForTesting void setSurfaceControlTransactionFactory( @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index 65972fb7df48..44cc563eadf4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -363,12 +363,10 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha mMotionHelper.synchronizePinnedStackBounds(); reloadResources(); - /* - if (mPipTaskOrganizer.isInPip()) { + if (mPipTransitionState.isInPip()) { // Recreate the dismiss target for the new orientation. mPipDismissTargetHandler.createOrUpdateDismissTarget(); } - */ } void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index 8ee087b33609..8f02c1b157b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -35,6 +35,7 @@ import android.animation.ValueAnimator; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.PictureInPictureParams; +import android.app.TaskInfo; import android.content.Context; import android.graphics.Point; import android.graphics.PointF; @@ -57,6 +58,7 @@ import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip2.animation.PipAlphaAnimator; import com.android.wm.shell.pip2.animation.PipEnterAnimator; @@ -74,8 +76,8 @@ public class PipTransition extends PipTransitionController implements private static final String TAG = PipTransition.class.getSimpleName(); // Used when for ENTERING_PIP state update. - private static final String PIP_TASK_TOKEN = "pip_task_token"; private static final String PIP_TASK_LEASH = "pip_task_leash"; + private static final String PIP_TASK_INFO = "pip_task_info"; // Used for PiP CHANGING_BOUNDS state update. static final String PIP_START_TX = "pip_start_tx"; @@ -245,8 +247,8 @@ public class PipTransition extends PipTransitionController implements // Update the PipTransitionState while supplying the PiP leash and token to be cached. Bundle extra = new Bundle(); - extra.putParcelable(PIP_TASK_TOKEN, pipChange.getContainer()); extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash()); + extra.putParcelable(PIP_TASK_INFO, pipChange.getTaskInfo()); mPipTransitionState.setState(PipTransitionState.ENTERING_PIP, extra); if (isInSwipePipToHomeTransition()) { @@ -899,10 +901,10 @@ public class PipTransition extends PipTransitionController implements Preconditions.checkState(extra != null, "No extra bundle for " + mPipTransitionState); - mPipTransitionState.setPipTaskToken(extra.getParcelable( - PIP_TASK_TOKEN, WindowContainerToken.class)); mPipTransitionState.setPinnedTaskLeash(extra.getParcelable( PIP_TASK_LEASH, SurfaceControl.class)); + mPipTransitionState.setPipTaskInfo(extra.getParcelable( + PIP_TASK_INFO, TaskInfo.class)); boolean hasValidTokenAndLeash = mPipTransitionState.getPipTaskToken() != null && mPipTransitionState.getPinnedTaskLeash() != null; @@ -915,16 +917,16 @@ public class PipTransition extends PipTransitionController implements mPipBoundsState.getBounds()); mPipBoundsState.saveReentryState(snapFraction); - mPipTransitionState.setPipTaskToken(null); mPipTransitionState.setPinnedTaskLeash(null); + mPipTransitionState.setPipTaskInfo(null); break; } } @Override public boolean isPackageActiveInPip(@Nullable String packageName) { - return packageName != null - && mPipBoundsState.getLastPipComponentName() != null - && packageName.equals(mPipBoundsState.getLastPipComponentName().getPackageName()); + final TaskInfo inPipTask = mPipTransitionState.getPipTaskInfo(); + return packageName != null && inPipTask != null && mPipTransitionState.isInPip() + && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java index 8e90bfee2636..6f9f40a487c7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java @@ -17,6 +17,7 @@ package com.android.wm.shell.pip2.phone; import android.annotation.IntDef; +import android.app.TaskInfo; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -133,17 +134,17 @@ public class PipTransitionState { private final Rect mSwipePipToHomeAppBounds = new Rect(); // - // Tokens and leashes + // Task related caches // - // pinned PiP task's WC token - @Nullable - private WindowContainerToken mPipTaskToken; - // pinned PiP task's leash @Nullable private SurfaceControl mPinnedTaskLeash; + // pinned PiP task info + @Nullable + private TaskInfo mPipTaskInfo; + // Overlay leash potentially used during swipe PiP to home transition; // if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid. @Nullable @@ -305,11 +306,7 @@ public class PipTransitionState { } @Nullable WindowContainerToken getPipTaskToken() { - return mPipTaskToken; - } - - public void setPipTaskToken(@Nullable WindowContainerToken token) { - mPipTaskToken = token; + return mPipTaskInfo != null ? mPipTaskInfo.getToken() : null; } @Nullable SurfaceControl getPinnedTaskLeash() { @@ -320,6 +317,14 @@ public class PipTransitionState { mPinnedTaskLeash = leash; } + @Nullable TaskInfo getPipTaskInfo() { + return mPipTaskInfo; + } + + void setPipTaskInfo(@Nullable TaskInfo pipTaskInfo) { + mPipTaskInfo = pipTaskInfo; + } + /** * @return true if either in swipe or button-nav fixed rotation. */ 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 4f0f6760a951..6e0e696e15fe 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 @@ -582,6 +582,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter, @Nullable WindowContainerToken hideTaskToken) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Legacy startTask does not support hide task token"); + if (isTaskInSplitScreenForeground(taskId)) return; final int[] result = new int[1]; IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { @Override 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 88a95660f098..07c157b4394c 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 @@ -501,6 +501,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** * Deactivates main stage by removing the stage from the top level split root (usually when a * task underneath gets removed from the stage root). + * This function should always be called as part of exiting split screen. * @param stageToTop stage which we want to put on top */ private void deactivateSplit(WindowContainerTransaction wct, @StageType int stageToTop) { @@ -833,7 +834,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); - addActivityOptions(options1, mSideStage); + StageTaskListener stageForTask1; + if (enableFlexibleSplit()) { + stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition, + true /*checkAllStagesIfNotActive*/); + } else { + stageForTask1 = mSideStage; + } + addActivityOptions(options1, stageForTask1); wct.sendPendingIntent(pendingIntent, fillInIntent, options1); prepareTasksForSplitScreen(new int[] {taskId}, wct); @@ -878,7 +886,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); - addActivityOptions(options1, mSideStage); + StageTaskListener stageForTask1; + if (enableFlexibleSplit()) { + stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition, + true /*checkAllStagesIfNotActive*/); + } else { + stageForTask1 = mSideStage; + } + addActivityOptions(options1, stageForTask1); wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); prepareTasksForSplitScreen(new int[] {taskId}, wct); @@ -1010,14 +1025,29 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setRootForceTranslucent(false, wct); options1 = options1 != null ? options1 : new Bundle(); - addActivityOptions(options1, mSideStage); + StageTaskListener stageForTask1; + if (enableFlexibleSplit()) { + stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition, + true /*checkAllStagesIfNotActive*/); + } else { + stageForTask1 = mSideStage; + } + addActivityOptions(options1, stageForTask1); if (shortcutInfo1 != null) { wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1); } else { wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1); } + + StageTaskListener stageForTask2; + if (enableFlexibleSplit()) { + stageForTask2 = mStageOrderOperator.getStageForLegacyPosition( + reverseSplitPosition(splitPosition), true /*checkAllStagesIfNotActive*/); + } else { + stageForTask2 = mMainStage; + } options2 = options2 != null ? options2 : new Bundle(); - addActivityOptions(options2, mMainStage); + addActivityOptions(options2, stageForTask2); if (shortcutInfo2 != null) { wct.startShortcut(mContext.getPackageName(), shortcutInfo2, options2); } else { @@ -1246,6 +1276,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Restore focus-ability to the windows and divider wct.setFocusable(mRootTaskInfo.token, true); + if (enableFlexibleSplit()) { + mStageOrderOperator.onDoubleTappedDivider(); + } setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(st -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt index b7b3c9b62a58..3fa8df40dfef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt @@ -38,6 +38,7 @@ import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_B import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_C import com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString import com.android.wm.shell.windowdecor.WindowDecorViewModel +import java.util.Collections import java.util.Optional /** @@ -148,6 +149,24 @@ class StageOrderOperator ( } /** + * This will swap the stages for the two stages on either side of the given divider. + * Note: This will keep [activeStages] and [allStages] in sync by swapping both of them + * If there are no [activeStages] then this will be a no-op. + * + * TODO(b/379984874): Take in a divider identifier to determine which array indices to swap + */ + fun onDoubleTappedDivider() { + if (activeStages.isEmpty()) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Stages not active, ignoring swap request") + return + } + + Collections.swap(activeStages, 0, 1) + Collections.swap(allStages, 0, 1) + } + + /** * Returns a legacy split position for the given stage. If no stages are active then this will * return [SPLIT_POSITION_UNDEFINED] */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 982fda0ddf36..aa954fbe5669 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -194,6 +194,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL @VisibleForTesting static void updateRelayoutParams( RelayoutParams relayoutParams, + @NonNull Context context, ActivityManager.RunningTaskInfo taskInfo, boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, @@ -206,9 +207,11 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL relayoutParams.mRunningTaskInfo = taskInfo; relayoutParams.mLayoutResId = R.layout.caption_window_decor; relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode()); - relayoutParams.mShadowRadiusId = hasGlobalFocus - ? R.dimen.freeform_decor_shadow_focused_thickness - : R.dimen.freeform_decor_shadow_unfocused_thickness; + relayoutParams.mShadowRadius = hasGlobalFocus + ? context.getResources().getDimensionPixelSize( + R.dimen.freeform_decor_shadow_focused_thickness) + : context.getResources().getDimensionPixelSize( + R.dimen.freeform_decor_shadow_unfocused_thickness); relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop; relayoutParams.mIsCaptionVisible = taskInfo.isFreeform() @@ -251,7 +254,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; final WindowContainerTransaction wct = new WindowContainerTransaction(); - updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw, + updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt index b8c91519422c..4d95cde1492f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt @@ -24,7 +24,7 @@ import android.view.WindowManager import android.window.TaskSnapshot import androidx.compose.ui.graphics.toArgb import com.android.wm.shell.shared.desktopmode.DesktopModeStatus -import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer +import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer import com.android.wm.shell.shared.split.SplitScreenConstants import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt index dd68105d28c1..101467df03ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt @@ -33,7 +33,7 @@ import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.DesktopRepository -import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer +import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index f48226930871..04ef7c1dc461 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -39,7 +39,7 @@ import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.Indica import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; -import static com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer.MANAGE_WINDOWS_MINIMUM_INSTANCES; +import static com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer.MANAGE_WINDOWS_MINIMUM_INSTANCES; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -107,6 +107,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger; import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum; @@ -174,6 +175,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; private final DesktopTasksController mDesktopTasksController; + private final DesktopImmersiveController mDesktopImmersiveController; private final InputManager mInputManager; private final InteractionJankMonitor mInteractionJankMonitor; private final MultiInstanceHelper mMultiInstanceHelper; @@ -249,6 +251,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, + DesktopImmersiveController desktopImmersiveController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, @@ -279,6 +282,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, syncQueue, transitions, desktopTasksController, + desktopImmersiveController, genericLinksParser, assistContentRequester, multiInstanceHelper, @@ -318,6 +322,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, + DesktopImmersiveController desktopImmersiveController, AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, @@ -351,6 +356,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mSyncQueue = syncQueue; mTransitions = transitions; mDesktopTasksController = desktopTasksController.get(); + mDesktopImmersiveController = desktopImmersiveController; mMultiInstanceHelper = multiInstanceHelper; mShellCommandHandler = shellCommandHandler; mWindowManager = windowManager; @@ -580,11 +586,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, if (decoration == null) { return; } - mInteractionJankMonitor.begin( - decoration.mTaskSurface, mContext, mMainHandler, - Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source); mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo, resizeTrigger, - DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent)); + DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent), () -> { + mInteractionJankMonitor.begin( + decoration.mTaskSurface, mContext, mMainHandler, + Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source); + return null; + }, () -> { + mInteractionJankMonitor.begin( + decoration.mTaskSurface, mContext, mMainHandler, + Cuj.CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW, source); + return null; + }); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } @@ -594,16 +607,33 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, if (decoration == null) { return; } - mDesktopTasksController.toggleDesktopTaskFullImmersiveState(decoration.mTaskInfo); + if (mDesktopRepository.isTaskInFullImmersiveState(taskId)) { + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_RESTORE); + mDesktopImmersiveController.moveTaskToNonImmersive( + decoration.mTaskInfo, DesktopImmersiveController.ExitReason.USER_INTERACTION); + } else { + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_IMMERSIVE); + mDesktopImmersiveController.moveTaskToImmersive(decoration.mTaskInfo); + } decoration.closeMaximizeMenu(); } - public void onSnapResize(int taskId, boolean left, InputMethod inputMethod) { + /** Snap-resize a task to the left or right side of the desktop. */ + public void onSnapResize(int taskId, boolean left, InputMethod inputMethod, boolean fromMenu) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); if (decoration == null) { return; } + if (fromMenu) { + final DesktopModeUiEventLogger.DesktopUiEventEnum event = left + ? DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_LEFT + : DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_RIGHT; + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, event); + } + mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler, Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable"); mDesktopTasksController.handleInstantSnapResizingTask( @@ -932,6 +962,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.closeMaximizeMenu(); } else { mHasLongClicked = true; + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_REVEAL_MENU); decoration.createMaximizeMenu(); } return true; @@ -1587,13 +1619,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, windowDecoration.setOnLeftSnapClickListener(() -> { onSnapResize(taskInfo.taskId, /* isLeft= */ true, DesktopModeEventLogger.getInputMethodFromMotionEvent( - touchEventListener.mMotionEvent)); + touchEventListener.mMotionEvent), /* fromMenu= */ true); return Unit.INSTANCE; }); windowDecoration.setOnRightSnapClickListener(() -> { onSnapResize(taskInfo.taskId, /* isLeft= */ false, DesktopModeEventLogger.getInputMethodFromMotionEvent( - touchEventListener.mMotionEvent)); + touchEventListener.mMotionEvent), /* fromMenu= */ true); return Unit.INSTANCE; }); windowDecoration.setOnToDesktopClickListener(desktopModeTransitionSource -> { @@ -1622,6 +1654,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, CompatUIController.launchUserAspectRatioSettings(mContext, taskInfo); return Unit.INSTANCE; }); + windowDecoration.setOnMaximizeHoverListener(() -> { + if (!windowDecoration.isMaximizeMenuActive()) { + mDesktopModeUiEventLogger.log(taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_REVEAL_MENU); + windowDecoration.createMaximizeMenu(); + } + return Unit.INSTANCE; + }); windowDecoration.setCaptionListeners( touchEventListener, touchEventListener, touchEventListener, touchEventListener); windowDecoration.setExclusionRegionListener(mExclusionRegionListener); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index d57044a4d27c..5eb031218ee1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -104,7 +104,7 @@ import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; -import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer; +import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder; @@ -155,6 +155,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private Function0<Unit> mOnNewWindowClickListener; private Function0<Unit> mOnManageWindowsClickListener; private Function0<Unit> mOnChangeAspectRatioClickListener; + private Function0<Unit> mOnMaximizeHoverListener; private DragPositioningCallback mDragPositioningCallback; private DragResizeInputListener mDragResizeListener; private Runnable mCurrentViewHostRunnable = null; @@ -370,6 +371,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mOnChangeAspectRatioClickListener = listener; } + /** Registers a listener to be called when the maximize header button is hovered. */ + void setOnMaximizeHoverListener(Function0<Unit> listener) { + mOnMaximizeHoverListener = listener; + } + void setCaptionListeners( View.OnClickListener onCaptionButtonClickListener, View.OnTouchListener onCaptionTouchListener, @@ -841,12 +847,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mOnCaptionGenericMotionListener, mAppName, mAppIconBitmap, - () -> { - if (!isMaximizeMenuActive()) { - createMaximizeMenu(); - } - return Unit.INSTANCE; - }); + mOnMaximizeHoverListener); } throw new IllegalArgumentException("Unexpected layout resource id"); } @@ -979,10 +980,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; } - if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ hasGlobalFocus)) { - relayoutParams.mShadowRadiusId = hasGlobalFocus - ? R.dimen.freeform_decor_shadow_focused_thickness - : R.dimen.freeform_decor_shadow_unfocused_thickness; + if (isAppHeader + && DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ hasGlobalFocus)) { + relayoutParams.mShadowRadius = hasGlobalFocus + ? context.getResources().getDimensionPixelSize( + R.dimen.freeform_decor_shadow_focused_thickness) + : context.getResources().getDimensionPixelSize( + R.dimen.freeform_decor_shadow_unfocused_thickness); + } else { + relayoutParams.mShadowRadius = INVALID_SHADOW_RADIUS; } relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop; @@ -1458,10 +1464,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @NonNull Function1<Integer, Unit> onIconClickListener ) { if (mTaskInfo.isFreeform()) { + // The menu uses display-wide coordinates for positioning, so make position the sum + // of task position and caption position. + final Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); mManageWindowsMenu = new DesktopHeaderManageWindowsMenu( mTaskInfo, - /* x= */ mResult.mCaptionX, - /* y= */ mResult.mCaptionY + mResult.mCaptionTopPadding, + /* x= */ taskBounds.left + mResult.mCaptionX, + /* y= */ taskBounds.top + mResult.mCaptionY + mResult.mCaptionTopPadding, mDisplayController, mRootTaskDisplayAreaOrganizer, mContext, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 852eee5f6672..584ee39ab317 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -17,7 +17,6 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.mandatorySystemGestures; @@ -110,6 +109,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> * Invalid corner radius that signifies that corner radius should not be set. */ static final int INVALID_CORNER_RADIUS = -1; + /** + * Invalid corner radius that signifies that shadow radius should not be set. + */ + static final int INVALID_SHADOW_RADIUS = -1; /** * System-wide context. Only used to create context with overridden configurations. @@ -439,16 +442,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> .setPosition(mTaskSurface, taskPosition.x, taskPosition.y); } - float shadowRadius; - if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { - // Shadow is not needed for fullscreen tasks - shadowRadius = 0; - } else { - shadowRadius = - loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId); + if (params.mShadowRadius != INVALID_SHADOW_RADIUS) { + startT.setShadowRadius(mTaskSurface, params.mShadowRadius); + finishT.setShadowRadius(mTaskSurface, params.mShadowRadius); } - startT.setShadowRadius(mTaskSurface, shadowRadius); - finishT.setShadowRadius(mTaskSurface, shadowRadius); if (params.mSetTaskVisibilityPositionAndCrop) { startT.show(mTaskSurface); @@ -851,8 +848,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> @InsetsSource.Flags int mInsetSourceFlags; final Region mDisplayExclusionRegion = Region.obtain(); - int mShadowRadiusId; - int mCornerRadius; + int mShadowRadius = INVALID_SHADOW_RADIUS; + int mCornerRadius = INVALID_CORNER_RADIUS; int mCaptionTopPadding; boolean mIsCaptionVisible; @@ -874,8 +871,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mInsetSourceFlags = 0; mDisplayExclusionRegion.setEmpty(); - mShadowRadiusId = Resources.ID_NULL; - mCornerRadius = 0; + mShadowRadius = INVALID_SHADOW_RADIUS; + mCornerRadius = INVALID_SHADOW_RADIUS; mCaptionTopPadding = 0; mIsCaptionVisible = false; diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/UnmaximizeAppWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/UnmaximizeAppWindowTest.kt new file mode 100644 index 000000000000..7a71d4bf7a27 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/UnmaximizeAppWindowTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.UnmaximizeAppWindow +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [UnmaximizeAppWindow]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class UnmaximizeAppWindowTest : UnmaximizeAppWindow() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnmaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnmaximizeAppWindow.kt new file mode 100644 index 000000000000..741125031b0a --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnmaximizeAppWindow.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.scenarios + +import android.app.Instrumentation +import android.tools.NavBar +import android.tools.Rotation +import android.tools.flicker.rules.ChangeDisplayOrientationRule +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +/** + * Testing "unmaximizing" a window e.g. making it get out of/exit a window that was already + * maximized. + */ +@Ignore("Test Base Class") +abstract class UnmaximizeAppWindow +constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) { + + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val testApp = if (isResizable) { + DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + } else { + DesktopModeAppHelper(NonResizeableAppHelper(instrumentation)) + } + + @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) + ChangeDisplayOrientationRule.setRotation(rotation) + testApp.enterDesktopMode(wmHelper, device) + // Press the buttonn once to setup app window to be maximized already + testApp.maximiseDesktopApp(wmHelper, device) + } + + @Test + open fun unmaximizeAppWindow() { + // Re-press button to exit maximized state + testApp.maximiseDesktopApp(wmHelper, device) + } + + @After + fun teardown() { + testApp.exit(wmHelper) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp index f40edaebec5e..ddbc681f7cac 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp @@ -266,26 +266,5 @@ test_module_config { test_suites: ["device-tests"], } -test_module_config { - name: "WMShellFlickerTestsPip-nonMatchParent", - base: "WMShellFlickerTestsPip", - include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.*"], - test_suites: ["device-tests"], -} - -test_module_config { - name: "WMShellFlickerTestsPip-BottomHalfExitPipToAppViaExpandButtonTest", - base: "WMShellFlickerTestsPip", - include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaExpandButtonTest"], - test_suites: ["device-tests"], -} - -test_module_config { - name: "WMShellFlickerTestsPip-BottomHalfExitPipToAppViaIntentTest", - base: "WMShellFlickerTestsPip", - include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaIntentTest"], - test_suites: ["device-tests"], -} - // End breakdowns for WMShellFlickerTestsPip module //////////////////////////////////////////////////////////////////////////////// diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt index 7861d201cd29..c37bf3579e93 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt @@ -40,6 +40,7 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { @Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + protected val pipApp = PipAppHelper(instrumentation) protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation) protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) @@ -62,11 +63,6 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { } } - /** - * Defines the test app to run PIP flicker test. - */ - protected open val pipApp = PipAppHelper(instrumentation) - /** Defines the transition used to run the test */ protected open val thisTransition: FlickerBuilder.() -> Unit = {} diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt deleted file mode 100644 index a6f29fc3452b..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip.nonmatchparent - -import android.platform.test.annotations.Presubmit -import android.platform.test.annotations.RequiresFlagsEnabled -import android.tools.flicker.legacy.LegacyFlickerTest -import android.tools.traces.component.ComponentNameMatcher -import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper -import com.android.server.wm.flicker.helpers.PipAppHelper -import com.android.window.flags.Flags -import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition -import org.junit.Test - -/** - * Base test class to verify PIP exit animation with an activity layout to the bottom half of - * the container. - */ -@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY) -abstract class BottomHalfExitPipToAppTransition(flicker: LegacyFlickerTest) : - ExitPipToAppTransition(flicker) { - - override val pipApp: PipAppHelper = BottomHalfPipAppHelper(instrumentation) - - @Presubmit - @Test - override fun showBothAppLayersThenHidePip() { - // Disabled since the BottomHalfPipActivity just covers half of the simple activity. - } - - @Presubmit - @Test - override fun showBothAppWindowsThenHidePip() { - // Disabled since the BottomHalfPipActivity just covers half of the simple activity. - } - - @Presubmit - @Test - override fun pipAppCoversFullScreenAtEnd() { - // Disabled since the BottomHalfPipActivity just covers half of the simple activity. - } - - /** - * Checks that the [testApp] and [pipApp] are always visible since the [pipApp] only covers - * half of screen. - */ - @Presubmit - @Test - fun showBothAppLayersDuringPipTransition() { - flicker.assertLayers { - isVisible(testApp) - .isVisible(pipApp.or(ComponentNameMatcher.TRANSITION_SNAPSHOT)) - } - } - - /** - * Checks that the [testApp] and [pipApp] are always visible since the [pipApp] only covers - * half of screen. - */ - @Presubmit - @Test - fun showBothAppWindowsDuringPipTransition() { - flicker.assertWm { - isAppWindowVisible(testApp) - .isAppWindowOnTop(pipApp) - .isAppWindowVisible(pipApp) - } - } - - /** - * Verify that the [testApp] and [pipApp] covers the entire screen at the end of PIP exit - * animation since the [pipApp] will use a bottom half layout. - */ - @Presubmit - @Test - fun testPlusPipAppCoversWindowFrameAtEnd() { - flicker.assertLayersEnd { - val pipRegion = visibleRegion(pipApp).region - visibleRegion(testApp).plus(pipRegion).coversExactly(displayBounds) - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt deleted file mode 100644 index f492bd444702..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.wm.shell.flicker.pip.nonmatchparent - -import android.tools.flicker.junit.FlickerParametersRunnerFactory -import android.tools.flicker.legacy.FlickerBuilder -import android.tools.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test expanding a pip window back to bottom half layout via the expand button - * - * To run this test: `atest WMShellFlickerTestsPip:BottomHalfExitPipToAppViaExpandButtonTest` - * - * Actions: - * ``` - * Launch an app in pip mode [bottomHalfPipApp], - * Launch another full screen mode [testApp] - * Expand [bottomHalfPipApp] app to bottom half layout by clicking on the pip window and - * then on the expand button - * ``` - * - * Notes: - * ``` - * 1. Some default assertions (e.g., nav bar, status bar and screen covered) - * are inherited [PipTransition] - * 2. Part of the test setup occurs automatically via - * [android.tools.flicker.legacy.runner.TransitionRunner], - * including configuring navigation mode, initial orientation and ensuring no - * apps are running before setup - * ``` - */ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class BottomHalfExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) : - BottomHalfExitPipToAppTransition(flicker) -{ - 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 transition idle and test and pip app still shows. - wmHelper.StateSyncBuilder().withLayerVisible(testApp).withLayerVisible(pipApp) - .withAppTransitionIdle().waitForAndVerify() - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt deleted file mode 100644 index a76a647348a1..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip.nonmatchparent - -import android.tools.flicker.junit.FlickerParametersRunnerFactory -import android.tools.flicker.legacy.FlickerBuilder -import android.tools.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test expanding a pip window back to bottom half layout via an intent - * - * To run this test: `atest WMShellFlickerTestsPip:BottomHalfExitPipToAppViaIntentTest` - * - * Actions: - * ``` - * Launch an app in pip mode [bottomHalfPipApp], - * Launch another full screen mode [testApp] - * Expand [bottomHalfPipApp] app to bottom half layout via an intent - * ``` - * - * Notes: - * ``` - * 1. Some default assertions (e.g., nav bar, status bar and screen covered) - * are inherited from [PipTransition] - * 2. Part of the test setup occurs automatically via - * [android.tools.flicker.legacy.runner.TransitionRunner], - * including configuring navigation mode, initial orientation and ensuring no - * apps are running before setup - * ``` - */ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class BottomHalfExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : - BottomHalfExitPipToAppTransition(flicker) -{ - 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 transition idle and test and pip app still shows. - wmHelper.StateSyncBuilder().withLayerVisible(testApp).withLayerVisible(pipApp) - .withAppTransitionIdle().waitForAndVerify() - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/transition/TransitionStateHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/transition/TransitionStateHolderTest.kt new file mode 100644 index 000000000000..64772d037383 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/transition/TransitionStateHolderTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.transition + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING +import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING +import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED +import com.android.wm.shell.sysui.ShellInit +import kotlin.test.assertNotNull +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.never + +/** + * Test class for {@link TransitionStateHolder} + * + * Usage: atest WMShellUnitTests:TransitionStateHolderTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TransitionStateHolderTest { + + lateinit var recentTransitionHandler: RecentsTransitionHandler + lateinit var shellInit: ShellInit + + @Before + fun before() { + recentTransitionHandler = mock(RecentsTransitionHandler::class.java) + shellInit = ShellInit(TestShellExecutor()) + } + + @Test + fun `No TransitionStateHolder listeners before initialization`() { + TransitionStateHolder(shellInit, recentTransitionHandler) + verify(recentTransitionHandler, never()).addTransitionStateListener(any()) + } + + @Test + fun `When TransitionStateHolder initialized a listener has been registered `() { + TransitionStateHolder(shellInit, recentTransitionHandler) + shellInit.init() + assertNotNull(recentsTransitionStateListener) + } + + @Test + fun `When TransitionStateHolder is created no recent animation running`() { + val holder = TransitionStateHolder(shellInit, recentTransitionHandler) + shellInit.init() + assertFalse(holder.isRecentsTransitionRunning()) + } + + @Test + fun `Recent animation running updates after callback value`() { + val holder = TransitionStateHolder(shellInit, recentTransitionHandler) + shellInit.init() + + assertFalse(holder.isRecentsTransitionRunning()) + + recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_NOT_RUNNING) + assertFalse(holder.isRecentsTransitionRunning()) + + recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_REQUESTED) + assertTrue(holder.isRecentsTransitionRunning()) + + recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING) + assertTrue(holder.isRecentsTransitionRunning()) + } + + private val recentsTransitionStateListener: RecentsTransitionStateListener + get() = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java).run { + verify(recentTransitionHandler).addTransitionStateListener(capture()) + value + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt new file mode 100644 index 000000000000..68d9bf9b926f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui.letterbox + +import android.content.Context +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn +import com.android.wm.shell.ShellTestCase +import java.util.function.Consumer +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.verify +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.verification.VerificationMode + +/** + * Tests for [LetterboxSurfaceBuilder]. + * + * Build/Install/Run: + * atest WMShellUnitTests:LetterboxSurfaceBuilderTest + */ +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LetterboxSurfaceBuilderTest : ShellTestCase() { + + @Test + fun `When surface is created mandatory methods are invoked`() { + runTestScenario { r -> + r.invokeBuilder() + + r.checkNameIsSet(expected = true) + r.checkCallSiteIsSet(expected = true) + r.checkSurfaceIsHidden(invoked = true, isHidden = true) + r.checkColorLayerIsSet(expected = true) + r.checkParentLeashIsSet(expected = true) + r.checkSetLayerIsInvoked(expected = true) + r.checkColorSpaceAgnosticIsSet(expected = true, value = true) + r.checkColorIsSetFromLetterboxConfiguration(expected = true) + } + } + + /** + * Runs a test scenario providing a Robot. + */ + fun runTestScenario(consumer: Consumer<LetterboxSurfaceBuilderRobotTest>) { + val robot = LetterboxSurfaceBuilderRobotTest(mContext) + consumer.accept(robot) + } + + class LetterboxSurfaceBuilderRobotTest(val ctx: Context) { + + private val letterboxConfiguration: LetterboxConfiguration + private val letterboxSurfaceBuilder: LetterboxSurfaceBuilder + private val tx: SurfaceControl.Transaction + private val parentLeash: SurfaceControl + private val surfaceBuilder: SurfaceControl.Builder + + companion object { + @JvmStatic + val TEST_SURFACE_NAME = "SurfaceForTest" + + @JvmStatic + val TEST_SURFACE_CALL_SITE = "CallSiteForTest" + } + + init { + letterboxConfiguration = LetterboxConfiguration(ctx) + letterboxSurfaceBuilder = LetterboxSurfaceBuilder(letterboxConfiguration) + tx = org.mockito.kotlin.mock<SurfaceControl.Transaction>() + doReturn(tx).`when`(tx).setLayer(anyOrNull(), anyOrNull()) + doReturn(tx).`when`(tx).setColorSpaceAgnostic(anyOrNull(), anyOrNull()) + parentLeash = org.mockito.kotlin.mock<SurfaceControl>() + surfaceBuilder = SurfaceControl.Builder() + spyOn(surfaceBuilder) + } + + fun invokeBuilder() { + letterboxSurfaceBuilder.createSurface( + tx, + parentLeash, + TEST_SURFACE_NAME, + TEST_SURFACE_CALL_SITE, + surfaceBuilder + ) + } + + fun checkNameIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()) + .setName(TEST_SURFACE_NAME) + } + + fun checkCallSiteIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()) + .setCallsite(TEST_SURFACE_CALL_SITE) + } + + fun checkSurfaceIsHidden(invoked: Boolean, isHidden: Boolean) { + verify(surfaceBuilder, invoked.asMode()) + .setHidden(isHidden) + } + + fun checkColorLayerIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()).setColorLayer() + } + + fun checkParentLeashIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()).setParent(parentLeash) + } + + fun checkSetLayerIsInvoked(expected: Boolean) { + verify(tx, expected.asMode()).setLayer(anyOrNull(), ArgumentMatchers.anyInt()) + } + + fun checkColorSpaceAgnosticIsSet(expected: Boolean, value: Boolean) { + verify(tx, expected.asMode()).setColorSpaceAgnostic(anyOrNull(), eq(value)) + } + + fun checkColorIsSetFromLetterboxConfiguration(expected: Boolean) { + val components = letterboxConfiguration.getBackgroundColorRgbArray() + verify(tx, expected.asMode()).setColor(anyOrNull(), eq(components)) + } + + private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never() + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt index 1ae1c3fc4563..9c6afcb8be63 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt @@ -16,10 +16,13 @@ package com.android.wm.shell.compatui.letterbox +import android.graphics.Point +import android.graphics.Rect import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner +import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CLOSE import androidx.test.filters.SmallTest import com.android.window.flags.Flags @@ -33,12 +36,12 @@ import java.util.function.Consumer import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.eq +import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times -import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.verify import org.mockito.verification.VerificationMode /** @@ -93,7 +96,7 @@ class LetterboxTransitionObserverTest : ShellTestCase() { r.creationEventDetected(expected = false) r.visibilityEventDetected(expected = false) r.destroyEventDetected(expected = false) - r.boundsEventDetected(expected = false) + r.updateSurfaceBoundsEventDetected(expected = false) } } } @@ -107,14 +110,23 @@ class LetterboxTransitionObserverTest : ShellTestCase() { inputBuilder { buildTransitionInfo() - r.createTopActivityChange(inputBuilder = this, isLetterboxed = true) + r.createTopActivityChange( + inputBuilder = this, + isLetterboxed = true, + taskPosition = Point(20, 30), + taskWidth = 200, + taskHeight = 300 + ) } validateOutput { r.creationEventDetected(expected = true) r.visibilityEventDetected(expected = true, visible = true) r.destroyEventDetected(expected = false) - r.boundsEventDetected(expected = true) + r.updateSurfaceBoundsEventDetected( + expected = true, + taskBounds = Rect(20, 30, 200, 300) + ) } } } @@ -135,7 +147,7 @@ class LetterboxTransitionObserverTest : ShellTestCase() { r.creationEventDetected(expected = false) r.visibilityEventDetected(expected = true, visible = false) r.destroyEventDetected(expected = false) - r.boundsEventDetected(expected = false) + r.updateSurfaceBoundsEventDetected(expected = false) } } } @@ -156,7 +168,7 @@ class LetterboxTransitionObserverTest : ShellTestCase() { r.destroyEventDetected(expected = true) r.creationEventDetected(expected = false) r.visibilityEventDetected(expected = false, visible = false) - r.boundsEventDetected(expected = false) + r.updateSurfaceBoundsEventDetected(expected = false) } } } @@ -189,10 +201,10 @@ class LetterboxTransitionObserverTest : ShellTestCase() { val observerFactory: () -> LetterboxTransitionObserver init { - executor = Mockito.mock(ShellExecutor::class.java) + executor = mock<ShellExecutor>() shellInit = ShellInit(executor) - transitions = Mockito.mock(Transitions::class.java) - letterboxController = Mockito.mock(LetterboxController::class.java) + transitions = mock<Transitions>() + letterboxController = mock<LetterboxController>() letterboxObserver = LetterboxTransitionObserver(shellInit, transitions, letterboxController) observerFactory = { letterboxObserver } @@ -203,67 +215,78 @@ class LetterboxTransitionObserverTest : ShellTestCase() { fun observer() = letterboxObserver fun checkObservableIsRegistered(expected: Boolean) { - Mockito.verify(transitions, expected.asMode()).registerObserver(observer()) + verify(transitions, expected.asMode()).registerObserver(observer()) } fun creationEventDetected( expected: Boolean, displayId: Int = DISPLAY_ID, taskId: Int = TASK_ID - ) { - Mockito.verify(letterboxController, expected.asMode()).createLetterboxSurface( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull(), - anyOrNull() - ) - } + ) = verify( + letterboxController, + expected.asMode() + ).createLetterboxSurface( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>(), + any<SurfaceControl>() + ) fun visibilityEventDetected( expected: Boolean, + visible: Boolean = true, displayId: Int = DISPLAY_ID, - taskId: Int = TASK_ID, - visible: Boolean? = null - ) { - Mockito.verify(letterboxController, expected.asMode()).updateLetterboxSurfaceVisibility( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull(), - visible.asMatcher() - ) - } + taskId: Int = TASK_ID + ) = verify(letterboxController, expected.asMode()).updateLetterboxSurfaceVisibility( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>(), + eq(visible) + ) fun destroyEventDetected( expected: Boolean, displayId: Int = DISPLAY_ID, taskId: Int = TASK_ID - ) { - Mockito.verify(letterboxController, expected.asMode()).destroyLetterboxSurface( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull() - ) - } - - fun boundsEventDetected( + ) = verify( + letterboxController, + expected.asMode() + ).destroyLetterboxSurface( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>() + ) + + fun updateSurfaceBoundsEventDetected( expected: Boolean, displayId: Int = DISPLAY_ID, - taskId: Int = TASK_ID - ) { - Mockito.verify(letterboxController, expected.asMode()).updateLetterboxSurfaceBounds( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull(), - anyOrNull() - ) - } + taskId: Int = TASK_ID, + taskBounds: Rect = Rect() + ) = verify( + letterboxController, + expected.asMode() + ).updateLetterboxSurfaceBounds( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>(), + eq(taskBounds) + ) fun createTopActivityChange( inputBuilder: TransitionObserverInputBuilder, isLetterboxed: Boolean = true, displayId: Int = DISPLAY_ID, - taskId: Int = TASK_ID + taskId: Int = TASK_ID, + taskPosition: Point = Point(), + taskWidth: Int = 0, + taskHeight: Int = 0 ) { - inputBuilder.addChange(changeTaskInfo = inputBuilder.createTaskInfo().apply { - appCompatTaskInfo.isTopActivityLetterboxed = isLetterboxed - this.taskId = taskId - this.displayId = displayId + inputBuilder.addChange(inputBuilder.createChange( + changeTaskInfo = inputBuilder.createTaskInfo().apply { + appCompatTaskInfo.isTopActivityLetterboxed = isLetterboxed + this.taskId = taskId + this.displayId = displayId + } + ).apply { + endRelOffset.x = taskPosition.x + endRelOffset.y = taskPosition.y + endAbsBounds.set(Rect(0, 0, taskWidth, taskHeight)) }) } @@ -279,16 +302,5 @@ class LetterboxTransitionObserverTest : ShellTestCase() { } private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never() - - private fun Boolean?.asMatcher(): Boolean = - if (this != null) eq(this) else any() - - private fun toLetterboxKeyMatcher(displayId: Int, taskId: Int): LetterboxKey { - if (displayId < 0 || taskId < 0) { - return any() - } else { - return eq(LetterboxKey(displayId, taskId)) - } - } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt index 887bdc259f54..605069533ad9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt @@ -53,10 +53,9 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS +import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout -import com.android.wm.shell.common.ShellExecutor -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel @@ -91,7 +90,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>() private val shellTaskOrganizer = mock<ShellTaskOrganizer>() private val focusTransitionObserver = mock<FocusTransitionObserver>() - private val testExecutor = mock<ShellExecutor>() + private val testExecutor = TestShellExecutor() private val inputManager = mock<InputManager>() private val displayController = mock<DisplayController>() private val displayLayout = mock<DisplayLayout>() @@ -134,6 +133,12 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { null }.whenever(inputManager).registerKeyGestureEventHandler(any()) shellInit.init() + + desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( + context, + Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), + inputManager, shellTaskOrganizer, focusTransitionObserver, testExecutor + ) } @After @@ -142,6 +147,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { runningTasks.clear() testScope.cancel() + testExecutor.flushAll() } @Test @@ -151,11 +157,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_USE_KEY_GESTURE_EVENT_HANDLER ) fun keyGestureMoveToNextDisplay_shouldMoveToNextDisplay() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) // Set up two display ids whenever(rootTaskDisplayAreaOrganizer.displayIds) .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY)) @@ -187,11 +188,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureSnapLeft_shouldSnapResizeTaskToLeft() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -205,9 +201,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) assertThat(result).isTrue() - verify(desktopModeWindowDecorViewModel).onSnapResize( - task.taskId, true, DesktopModeEventLogger.Companion.InputMethod.KEYBOARD - ) + assertThat(testExecutor.callbacks.size).isEqualTo(1) } @Test @@ -216,11 +210,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureSnapRight_shouldSnapResizeTaskToRight() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -234,9 +223,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) assertThat(result).isTrue() - verify(desktopModeWindowDecorViewModel).onSnapResize( - task.taskId, false, DesktopModeEventLogger.Companion.InputMethod.KEYBOARD - ) + assertThat(testExecutor.callbacks.size).isEqualTo(1) } @Test @@ -245,11 +232,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureToggleFreeformWindowSize_shouldToggleTaskSize() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -263,11 +245,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) assertThat(result).isTrue() - verify(desktopTasksController).toggleDesktopTaskSize( - task, - ResizeTrigger.MAXIMIZE_MENU, - DesktopModeEventLogger.Companion.InputMethod.KEYBOARD - ) + assertThat(testExecutor.callbacks.size).isEqualTo(1) } @Test @@ -276,11 +254,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureMinimizeFreeformWindow_shouldMinimizeTask() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -294,7 +267,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) assertThat(result).isTrue() - verify(desktopTasksController).minimizeTask(task) + assertThat(testExecutor.callbacks.size).isEqualTo(1) } private fun setUpFreeformTask( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index fc31d08b4571..5c0027220ec9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -3751,26 +3751,6 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun toggleImmersive_enter_movesToImmersive() { - val task = setUpFreeformTask(DEFAULT_DISPLAY) - taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, false /* immersive */) - - controller.toggleDesktopTaskFullImmersiveState(task) - - verify(mMockDesktopImmersiveController).moveTaskToImmersive(task) - } - - @Test - fun toggleImmersive_exit_movesToNonImmersive() { - val task = setUpFreeformTask(DEFAULT_DISPLAY) - taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, true /* immersive */) - - controller.toggleDesktopTaskFullImmersiveState(task) - - verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any()) - } - - @Test @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() { val task = setUpFreeformTask(DEFAULT_DISPLAY) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java index cab625216236..57b6d7f0ac2f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java @@ -42,8 +42,10 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import com.android.wm.shell.pip2.animation.PipAlphaAnimator; @@ -56,6 +58,8 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Optional; + /** * Unit test against {@link PipScheduler} */ @@ -79,6 +83,8 @@ public class PipSchedulerTest { @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory; @Mock private SurfaceControl.Transaction mMockTransaction; @Mock private PipAlphaAnimator mMockAlphaAnimator; + @Mock private Optional<DesktopRepository> mMockOptionalDesktopRepository; + @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; @Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor; @Captor private ArgumentCaptor<WindowContainerTransaction> mWctArgumentCaptor; @@ -96,7 +102,8 @@ public class PipSchedulerTest { .thenReturn(mMockTransaction); mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor, - mMockPipTransitionState); + mMockPipTransitionState, mMockOptionalDesktopRepository, + mRootTaskDisplayAreaOrganizer); mPipScheduler.setPipTransitionController(mMockPipTransitionController); mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory); mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, tx, direction) -> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt index 3e26ee0deed0..0e15668a05a7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt @@ -36,7 +36,6 @@ import android.window.TransitionInfo.TransitionMode import android.window.WindowContainerToken import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn import com.android.wm.shell.transition.Transitions.TransitionObserver -import org.mockito.Mockito import org.mockito.kotlin.mock @DslMarker @@ -93,10 +92,10 @@ class TransitionObserverTestContext : TransitionObserverTestStep { */ class TransitionObserverInputBuilder : TransitionObserverTestStep { - private val transition = Mockito.mock(IBinder::class.java) + private val transition = mock<IBinder>() private var transitionInfo: TransitionInfo? = null - private val startTransaction = Mockito.mock(Transaction::class.java) - private val finishTransaction = Mockito.mock(Transaction::class.java) + private val startTransaction = mock<Transaction>() + private val finishTransaction = mock<Transaction>() fun buildTransitionInfo( @TransitionType type: Int = TRANSIT_NONE, @@ -143,7 +142,7 @@ class TransitionObserverInputBuilder : TransitionObserverTestStep { taskId = id displayId = DEFAULT_DISPLAY configuration.windowConfiguration.windowingMode = windowingMode - token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)) + token = WindowContainerToken(mock<IWindowContainerToken>()) baseIntent = Intent().apply { component = ComponentName("package", "component.name") } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt index 59141ca39487..b856a28e54db 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt @@ -48,6 +48,7 @@ class CaptionWindowDecorationTests : ShellTestCase() { CaptionWindowDecoration.updateRelayoutParams( relayoutParams, + mContext, taskInfo, true, false, @@ -71,6 +72,7 @@ class CaptionWindowDecorationTests : ShellTestCase() { CaptionWindowDecoration.updateRelayoutParams( relayoutParams, + mContext, taskInfo, true, false, @@ -90,6 +92,7 @@ class CaptionWindowDecorationTests : ShellTestCase() { val relayoutParams = WindowDecoration.RelayoutParams() CaptionWindowDecoration.updateRelayoutParams( relayoutParams, + mContext, taskInfo, true, false, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index b873162e6fe3..153be07bd204 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -59,6 +59,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.window.flags.Flags import com.android.wm.shell.R +import com.android.wm.shell.desktopmode.DesktopImmersiveController import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition @@ -397,9 +398,11 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest maxOrRestoreListenerCaptor.value.invoke() verify(mockDesktopTasksController).toggleDesktopTaskSize( - decor.mTaskInfo, - ResizeTrigger.MAXIMIZE_MENU, - InputMethod.UNKNOWN_INPUT_METHOD + eq(decor.mTaskInfo), + eq(ResizeTrigger.MAXIMIZE_MENU), + eq(InputMethod.UNKNOWN_INPUT_METHOD), + any(), + any() ) } @@ -1000,7 +1003,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - fun testMaximizeButtonClick_requestingImmersive_togglesDesktopImmersiveState() { + fun testImmersiveButtonClick_entersImmersiveMode() { val onClickListenerCaptor = forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener> val decor = createOpenTaskDecoration( @@ -1010,11 +1013,35 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest ) val view = mock(View::class.java) whenever(view.id).thenReturn(R.id.maximize_window) + whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId)) + .thenReturn(false) onClickListenerCaptor.value.onClick(view) - verify(mockDesktopTasksController) - .toggleDesktopTaskFullImmersiveState(decor.mTaskInfo) + verify(mockDesktopImmersiveController).moveTaskToImmersive(decor.mTaskInfo) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun testImmersiveRestoreButtonClick_exitsImmersiveMode() { + val onClickListenerCaptor = forClass(View.OnClickListener::class.java) + as ArgumentCaptor<View.OnClickListener> + val decor = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + onCaptionButtonClickListener = onClickListenerCaptor, + requestingImmersive = true, + ) + val view = mock(View::class.java) + whenever(view.id).thenReturn(R.id.maximize_window) + whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId)) + .thenReturn(true) + + onClickListenerCaptor.value.onClick(view) + + verify(mockDesktopImmersiveController).moveTaskToNonImmersive( + decor.mTaskInfo, + DesktopImmersiveController.ExitReason.USER_INTERACTION + ) } @Test @@ -1034,26 +1061,29 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest verify(mockDesktopTasksController) .toggleDesktopTaskSize( - decor.mTaskInfo, - ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD + eq(decor.mTaskInfo), + eq(ResizeTrigger.MAXIMIZE_BUTTON), + eq(InputMethod.UNKNOWN_INPUT_METHOD), + any(), + any(), ) } @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - fun testImmersiveClick_togglesImmersiveState() { + fun testImmersiveMenuOptionClick_entersImmersiveMode() { val onImmersiveClickCaptor = argumentCaptor<() -> Unit>() val decor = createOpenTaskDecoration( windowingMode = WINDOWING_MODE_FREEFORM, onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor, requestingImmersive = true, ) + whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId)) + .thenReturn(false) onImmersiveClickCaptor.firstValue() - verify(mockDesktopTasksController) - .toggleDesktopTaskFullImmersiveState(decor.mTaskInfo) + verify(mockDesktopImmersiveController).moveTaskToImmersive(decor.mTaskInfo) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt index 1670f2a6815b..080f496593cf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt @@ -54,6 +54,7 @@ import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler +import com.android.wm.shell.desktopmode.DesktopImmersiveController import com.android.wm.shell.desktopmode.DesktopModeEventLogger import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger import com.android.wm.shell.desktopmode.DesktopRepository @@ -112,6 +113,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { protected val displayInsetsController = mock<DisplayInsetsController>() protected val mockSyncQueue = mock<SyncTransactionQueue>() protected val mockDesktopTasksController = mock<DesktopTasksController>() + protected val mockDesktopImmersiveController = mock<DesktopImmersiveController>() protected val mockInputMonitor = mock<InputMonitor>() protected val mockTransitions = mock<Transitions>() internal val mockInputMonitorFactory = @@ -183,6 +185,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { mockSyncQueue, mockTransitions, Optional.of(mockDesktopTasksController), + mockDesktopImmersiveController, mockGenericLinksParser, mockAssistContentRequester, mockMultiInstanceHelper, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index e390fbbd751f..03c7c9857d8f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -61,7 +61,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PointF; import android.graphics.Rect; @@ -295,8 +294,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { } @Test - public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreEnabled() { + public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); RelayoutParams relayoutParams = new RelayoutParams(); DesktopModeWindowDecoration.updateRelayoutParams( @@ -309,7 +309,46 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* hasGlobalFocus= */ true, mExclusionRegion); - assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL); + assertThat(relayoutParams.mShadowRadius) + .isNotEqualTo(WindowDecoration.INVALID_SHADOW_RADIUS); + } + + @Test + public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false, + /* isStatusBarVisible */ true, + /* isKeyguardVisibleAndOccluded */ false, + /* inFullImmersiveMode */ false, + new InsetsState(), + /* hasGlobalFocus= */ true, + mExclusionRegion); + + assertThat(relayoutParams.mShadowRadius).isEqualTo(WindowDecoration.INVALID_SHADOW_RADIUS); + } + + @Test + public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false, + /* isStatusBarVisible */ true, + /* isKeyguardVisibleAndOccluded */ false, + /* inFullImmersiveMode */ false, + new InsetsState(), + /* hasGlobalFocus= */ true, + mExclusionRegion); + + assertThat(relayoutParams.mShadowRadius).isEqualTo(WindowDecoration.INVALID_SHADOW_RADIUS); } @Test @@ -359,6 +398,29 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { } @Test + public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + fillRoundedCornersResources(/* fillValue= */ 30); + RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false, + /* isStatusBarVisible */ true, + /* isKeyguardVisibleAndOccluded */ false, + /* inFullImmersiveMode */ false, + new InsetsState(), + /* hasGlobalFocus= */ true, + mExclusionRegion); + + assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS); + } + + @Test @EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY) public void updateRelayoutParams_appHeader_usesTaskDensity() { final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 534803db5fe0..04b2be0b1a25 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -114,6 +114,7 @@ public class WindowDecorationTests extends ShellTestCase { private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400); private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60); private static final int CORNER_RADIUS = 20; + private static final int SHADOW_RADIUS = 10; private static final int STATUS_BAR_INSET_SOURCE_ID = 0; @Rule @@ -162,7 +163,7 @@ public class WindowDecorationTests extends ShellTestCase { mRelayoutParams.mLayoutResId = 0; mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height; mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width; - mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius; + mRelayoutParams.mShadowRadius = SHADOW_RADIUS; mRelayoutParams.mCornerRadius = CORNER_RADIUS; when(mMockDisplayController.getDisplay(Display.DEFAULT_DISPLAY)) @@ -280,7 +281,7 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS); verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS); - verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, 10); + verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, SHADOW_RADIUS); assertEquals(300, mRelayoutResult.mWidth); assertEquals(100, mRelayoutResult.mHeight); diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index 2d812d675fdc..4dfe05377a48 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -76,6 +76,7 @@ Typeface* Typeface::createRelative(Typeface* src, Typeface::Style style) { result->fBaseWeight = resolvedFace->fBaseWeight; result->fAPIStyle = style; result->fStyle = computeRelativeStyle(result->fBaseWeight, style); + result->fIsVariationInstance = resolvedFace->fIsVariationInstance; } return result; } @@ -88,6 +89,7 @@ Typeface* Typeface::createAbsolute(Typeface* base, int weight, bool italic) { result->fBaseWeight = resolvedFace->fBaseWeight; result->fAPIStyle = computeAPIStyle(weight, italic); result->fStyle = computeMinikinStyle(weight, italic); + result->fIsVariationInstance = resolvedFace->fIsVariationInstance; } return result; } @@ -109,6 +111,7 @@ Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src, result->fBaseWeight = resolvedFace->fBaseWeight; result->fAPIStyle = resolvedFace->fAPIStyle; result->fStyle = resolvedFace->fStyle; + result->fIsVariationInstance = true; } return result; } @@ -121,6 +124,7 @@ Typeface* Typeface::createWithDifferentBaseWeight(Typeface* src, int weight) { result->fBaseWeight = weight; result->fAPIStyle = resolvedFace->fAPIStyle; result->fStyle = computeRelativeStyle(weight, result->fAPIStyle); + result->fIsVariationInstance = resolvedFace->fIsVariationInstance; } return result; } @@ -170,6 +174,7 @@ Typeface* Typeface::createFromFamilies(std::vector<std::shared_ptr<minikin::Font result->fBaseWeight = weight; result->fAPIStyle = computeAPIStyle(weight, italic); result->fStyle = computeMinikinStyle(weight, italic); + result->fIsVariationInstance = false; return result; } diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h index 2c96c1ad80fe..97d1bf4ef011 100644 --- a/libs/hwui/hwui/Typeface.h +++ b/libs/hwui/hwui/Typeface.h @@ -44,6 +44,9 @@ public: // base weight in CSS-style units, 1..1000 int fBaseWeight; + // True if the Typeface is already created for variation settings. + bool fIsVariationInstance; + static const Typeface* resolveDefault(const Typeface* src); // The following three functions create new Typeface from an existing Typeface with a different diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index d9dc8eb8c1b1..a0291a9e806f 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -594,14 +594,12 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, Matrix4 transform; SkIRect clipBounds; uirenderer::Rect initialClipBounds; - const auto clipFlags = props.getClippingFlags(); if (enableClip) { - if (clipFlags) { - props.getClippingRectForFlags(clipFlags, &initialClipBounds); - } else { - // Works for RenderNode::damageSelf() - initialClipBounds.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); - } + // SurfaceView never draws beyond its bounds regardless of if it can or not, + // so if clip-to-bounds is disabled just use the bounds as the starting point + // regardless + const auto clipFlags = props.getClippingFlags(); + props.getClippingRectForFlags(clipFlags | CLIP_TO_BOUNDS, &initialClipBounds); clipBounds = info.damageAccumulator ->computeClipAndTransform(initialClipBounds.toSkRect(), &transform) diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp index 5f693462af91..c73598960551 100644 --- a/libs/hwui/jni/text/TextShaper.cpp +++ b/libs/hwui/jni/text/TextShaper.cpp @@ -68,7 +68,7 @@ static void releaseLayout(jlong ptr) { static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int count, int contextStart, int contextCount, minikin::Bidi bidiFlags, const Paint& paint, const Typeface* typeface) { - + const Typeface* resolvedFace = Typeface::resolveDefault(typeface); minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(&paint, typeface); minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface, @@ -103,8 +103,16 @@ static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int cou fontId = it->second; // We've seen it. } else { fontId = fonts.size(); // This is new to us. Create new one. - std::shared_ptr<minikin::Font> font = std::make_shared<minikin::Font>( - fakedFont.font, fakedFont.fakery.variationSettings()); + std::shared_ptr<minikin::Font> font; + if (resolvedFace->fIsVariationInstance) { + // The optimization for target SDK 35 or before because the variation instance + // is already created and no runtime variation resolution happens on such + // environment. + font = fakedFont.font; + } else { + font = std::make_shared<minikin::Font>(fakedFont.font, + fakedFont.fakery.variationSettings()); + } fonts.push_back(reinterpret_cast<jlong>(new FontWrapper(std::move(font)))); fakedToFontIds.insert(std::make_pair(fakedFont, fontId)); } diff --git a/location/java/android/location/altitude/AltitudeConverter.java b/location/java/android/location/altitude/AltitudeConverter.java index b9fe804c504c..3a4e70dd5950 100644 --- a/location/java/android/location/altitude/AltitudeConverter.java +++ b/location/java/android/location/altitude/AltitudeConverter.java @@ -26,8 +26,8 @@ import android.location.Location; import android.location.flags.Flags; import com.android.internal.location.altitude.GeoidMap; -import com.android.internal.location.altitude.S2CellIdUtils; import com.android.internal.location.altitude.nano.MapParamsProto; +import com.android.internal.location.geometry.S2CellIdUtils; import com.android.internal.util.Preconditions; import java.io.IOException; diff --git a/location/java/com/android/internal/location/altitude/GeoidMap.java b/location/java/com/android/internal/location/altitude/GeoidMap.java index df9ca97817f7..d77fb9e011b2 100644 --- a/location/java/com/android/internal/location/altitude/GeoidMap.java +++ b/location/java/com/android/internal/location/altitude/GeoidMap.java @@ -26,6 +26,7 @@ import android.util.LruCache; import com.android.internal.annotations.GuardedBy; import com.android.internal.location.altitude.nano.MapParamsProto; import com.android.internal.location.altitude.nano.S2TileProto; +import com.android.internal.location.geometry.S2CellIdUtils; import com.android.internal.util.Preconditions; import java.io.IOException; diff --git a/location/java/com/android/internal/location/altitude/S2CellIdUtils.java b/location/java/com/android/internal/location/geometry/S2CellIdUtils.java index 08bcda41790e..fbdaf49f260e 100644 --- a/location/java/com/android/internal/location/altitude/S2CellIdUtils.java +++ b/location/java/com/android/internal/location/geometry/S2CellIdUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.location.altitude; +package com.android.internal.location.geometry; import android.annotation.NonNull; @@ -48,12 +48,22 @@ public final class S2CellIdUtils { private static final double UV_LIMIT = calculateUvLimit(); private static final UvTransform[] UV_TRANSFORMS = createUvTransforms(); private static final XyzTransform[] XYZ_TRANSFORMS = createXyzTransforms(); + private static final long MAX_SI_TI = 1L << (MAX_LEVEL + 1); // Used to encode (i, j, o) coordinates into primitive longs. private static final int I_SHIFT = 33; private static final int J_SHIFT = 2; private static final long J_MASK = (1L << 31) - 1; + // Used to insert latitude and longitude values into arrays. + public static final int LAT_LNG_MIN_LENGTH = 2; + public static final int LAT_INDEX = 0; + public static final int LNG_INDEX = 1; + + // Used to encode (si, ti) coordinates into primitive longs. + private static final int SI_SHIFT = 32; + private static final long TI_MASK = (1L << 32) - 1; + static { initLookupCells(); } @@ -63,6 +73,130 @@ public final class S2CellIdUtils { } /** + * Inserts into {@code latLngDegrees} the centroid latitude and longitude, in that order and + * both measured in degrees, for the specified S2 cell ID. This array must be non-null and of + * minimum length two. A reference to this array is returned. + * + * <p>Behavior is undefined for invalid S2 cell IDs. + */ + public static double[] toLatLngDegrees(long s2CellId, double[] latLngDegrees) { + // Used latLngDegrees as scratchpad for toLatLngRadians(long, double[]). + final double[] latLngRadians = latLngDegrees; + toLatLngRadians(s2CellId, latLngRadians); + latLngDegrees[LAT_INDEX] = Math.toDegrees(latLngRadians[LAT_INDEX]); + latLngDegrees[LNG_INDEX] = Math.toDegrees(latLngRadians[LNG_INDEX]); + return latLngDegrees; + } + + + /** + * Inserts into {@code latLngRadians} the centroid latitude and longitude, in that order and + * both measured in radians, for the specified S2 cell ID. This array must be non-null and of + * minimum length two. A reference to this array is returned. + * + * <p>Behavior is undefined for invalid S2 cell IDs. + */ + public static double[] toLatLngRadians(long s2CellId, double[] latLngRadians) { + checkNotNull(latLngRadians); + checkLengthGreaterThanOrEqualTo(LAT_LNG_MIN_LENGTH, latLngRadians.length); + + final long siTi = toSiTi(s2CellId); + final double u = siTiToU(siTi); + final double v = siTiToV(siTi); + + final int face = getFace(s2CellId); + final XyzTransform xyzTransform = faceToXyzTransform(face); + final double x = xyzTransform.uvToX(u, v); + final double y = xyzTransform.uvToY(u, v); + final double z = xyzTransform.uvToZ(u, v); + + latLngRadians[LAT_INDEX] = xyzToLatRadians(x, y, z); + latLngRadians[LNG_INDEX] = xyzToLngRadians(x, y); + return latLngRadians; + } + + private static long toSiTi(long s2CellId) { + final long ijo = toIjo(s2CellId); + final int i = ijoToI(ijo); + final int j = ijoToJ(ijo); + int delta = isLeaf(s2CellId) ? 1 : (((i ^ (((int) s2CellId) >>> 2)) & 1) != 0) ? 2 : 0; + return (((long) (2 * i + delta)) << SI_SHIFT) | ((2 * j + delta) & TI_MASK); + } + + private static int siTiToSi(long siTi) { + return (int) (siTi >> SI_SHIFT); + } + + private static int siTiToTi(long siTi) { + return (int) siTi; + } + + private static double siTiToU(long siTi) { + final int si = siTiToSi(siTi); + return siToU(si); + } + + private static double siTiToV(long siTi) { + final int ti = siTiToTi(siTi); + return tiToV(ti); + } + + private static double siToU(long si) { + final double s = (1.0 / MAX_SI_TI) * si; + if (s >= 0.5) { + return (1 / 3.) * (4 * s * s - 1); + } + return (1 / 3.) * (1 - 4 * (1 - s) * (1 - s)); + } + + private static double tiToV(long ti) { + // Same calculation as siToU. + return siToU(ti); + } + + private static XyzTransform faceToXyzTransform(int face) { + // We map illegal face indices to the largest face index to preserve legacy behavior, i.e., + // we do not want to throw an index out of bounds exception. Note that getFace(s2CellId) is + // guaranteed to return a non-negative face index even for invalid S2 cells, so it is + // sufficient to just map all face indices greater than the largest face index to the + // largest face index. + return XYZ_TRANSFORMS[Math.min(NUM_FACES - 1, face)]; + } + + private static double xyzToLngRadians(double x, double y) { + return Math.atan2(y, x); + } + + private static double xyzToLatRadians(double x, double y, double z) { + return Math.atan2(z, Math.sqrt(x * x + y * y)); + } + + private static void checkNotNull(Object object) { + if (object == null) { + throw new NullPointerException("Given array cannot be null."); + } + } + + private static void checkLengthGreaterThanOrEqualTo(int minLength, int actualLength) { + if (actualLength < minLength) { + throw new IllegalArgumentException( + "Given array of length " + actualLength + " needs to be of minimum length " + + minLength); + } + } + + /** + * Returns true if the provided S2 cell contains the provided latitude/longitude, both measured + * in degrees. + */ + public static boolean containsLatLngDegrees(long s2CellId, double latDegrees, + double lngDegrees) { + int level = getLevel(s2CellId); + long leafCellId = fromLatLngDegrees(latDegrees, lngDegrees); + return (getParent(leafCellId, level) == s2CellId); + } + + /** * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in * degrees. */ @@ -176,7 +310,7 @@ public final class S2CellIdUtils { * Returns the level of the specified S2 cell. The returned level is in [0, 30] for valid * S2 cell IDs. Behavior is undefined for invalid S2 cell IDs. */ - static int getLevel(long s2CellId) { + public static int getLevel(long s2CellId) { if (isLeaf(s2CellId)) { return MAX_LEVEL; } @@ -197,12 +331,12 @@ public final class S2CellIdUtils { * Returns the ID of the first S2 cell in a traversal of the children S2 cells at the specified * level, in Hilbert curve order. */ - static long getTraversalStart(long s2CellId, int level) { + public static long getTraversalStart(long s2CellId, int level) { return s2CellId - getLowestOnBit(s2CellId) + getLowestOnBitForLevel(level); } /** Returns the ID of the next S2 cell at the same level along the Hilbert curve. */ - static long getTraversalNext(long s2CellId) { + public static long getTraversalNext(long s2CellId) { return s2CellId + (getLowestOnBit(s2CellId) << 1); } @@ -211,7 +345,7 @@ public final class S2CellIdUtils { * lower levels (i.e., larger cells) are encoded into fewer characters. */ @NonNull - static String getToken(long s2CellId) { + public static String getToken(long s2CellId) { if (s2CellId == 0) { return "X"; } diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index 66da03144a7d..dba9cc95d902 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -283,8 +283,19 @@ public final class AudioPlaybackConfiguration implements Parcelable { * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO. */ @SystemApi + @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int MUTED_BY_OP_PLAY_AUDIO = (1 << 3); + /** + * @hide + * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO. + * @deprecated see {@link MUTED_BY_OP_PLAY_AUDIO} + */ + @SystemApi + @Deprecated + @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public static final int MUTED_BY_APP_OPS = (1 << 3); + public static final int MUTED_BY_APP_OPS = MUTED_BY_OP_PLAY_AUDIO; /** * @hide * Flag used when muted by client volume. @@ -324,7 +335,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { @IntDef( flag = true, value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED, - MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER, + MUTED_BY_OP_PLAY_AUDIO, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER, MUTED_BY_PORT_VOLUME, MUTED_BY_OP_CONTROL_AUDIO}) @Retention(RetentionPolicy.SOURCE) public @interface PlayerMuteEvent { @@ -770,7 +781,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { private boolean isMuteAffectingActiveState() { return (mMutedState & MUTED_BY_CLIENT_VOLUME) != 0 || (mMutedState & MUTED_BY_VOLUME_SHAPER) != 0 - || (mMutedState & MUTED_BY_APP_OPS) != 0; + || (mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0; } /** @@ -911,8 +922,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { if ((mMutedState & MUTED_BY_STREAM_MUTED) != 0) { apcToString.append("streamMute "); } - if ((mMutedState & MUTED_BY_APP_OPS) != 0) { - apcToString.append("appOps "); + if ((mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0) { + apcToString.append("opPlayAudio "); } if ((mMutedState & MUTED_BY_CLIENT_VOLUME) != 0) { apcToString.append("clientVolume "); diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java index 3fac74b2a37d..7b0bd04f3559 100644 --- a/media/java/android/media/quality/MediaQualityContract.java +++ b/media/java/android/media/quality/MediaQualityContract.java @@ -74,6 +74,130 @@ public class MediaQualityContract { */ public static final String PARAMETER_SATURATION = "saturation"; + /** + * @hide + */ + public static final String PARAMETER_COLOR = "color"; + /** + * @hide + */ + public static final String PARAMETER_HUE = "hue"; + + /** + * @hide + */ + public static final String PARAMETER_BACKLIGHT = "backlight"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_REDO_FFSET = "color_tuner_red_offset"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_GREEN_OFFSET = "color_tuner_green_offset"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_BLUE_OFFSET = "color_tuner_blue_offset"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain"; + + /** + * @hide + */ + public static final String PARAMETER_AI_PQ = "ai_pq"; + + /** + * @hide + */ + public static final String PARAMETER_AI_SUPER_RESOLUTION = "ai_super_resolution"; + + /** + * @hide + */ + public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction"; + + /** + * @hide + */ + public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction"; + + /** + * @hide + */ + public static final String PARAMETER_FLESH_TONE = "flesh_tone"; + + /** + * @hide + */ + public static final String PARAMETER_DECONTOUR = "decontour"; + + /** + * @hide + */ + public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control"; + + /** + * @hide + */ + public static final String PARAMETER_FILM_MODE = "film_mode"; + + /** + * @hide + */ + public static final String PARAMETER_BLACK_STRETCH = "black_stretch"; + + /** + * @hide + */ + public static final String PARAMETER_BLUE_STRETCH = "blue_stretch"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TUNE = "color_tune"; + + /** + * @hide + */ + public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature"; + + /** + * @hide + */ + public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming"; + private PictureQuality() { } } @@ -105,6 +229,129 @@ public class MediaQualityContract { */ public static final String PARAMETER_TREBLE = "treble"; + /** + * @hide + */ + public static final String PARAMETER_SOUND_MODE = "sound_mode"; + + /** + * @hide + */ + public static final String PARAMETER_SURROUND_SOUND = "surround_sound"; + + /** + * @hide + */ + public static final String PARAMETER_EQUALIZER_DETAIL = "equalizer_detail"; + + /** + * @hide + */ + public static final String PARAMETER_SPEAKERS = "speakers"; + + /** + * @hide + */ + public static final String PARAMETER_SPEAKERS_DELAY = "speakers_delay"; + + /** + * @hide + */ + public static final String PARAMETER_EARC = "earc"; + + /** + * @hide + */ + public static final String PARAMETER_AUTO_VOLUME_CONTROL = "auto_volume_control"; + + /** + * @hide + */ + public static final String PARAMETER_DOWN_MIX_MODE = "down_mix_mode"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_DRC = "dts_drc"; + + /** + * @hide + */ + public static final String PARAMETER_DOLBY_AUDIO_PROCESSING = "dolby_audio_processing"; + + /** + * @hide + */ + public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE = + "dolby_audio_processing_sound_mode"; + + /** + * @hide + */ + public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER = + "dolby_audio_processing_volume_leveler"; + + /** + * @hide + */ + public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER = + "dolby_audio_processing_surround_virtualizer"; + + /** + * @hide + */ + public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS = + "dolby_audio_processing_dolby_atmos"; + + /** + * @hide + */ + public static final String PARAMETER_DIALOGUE_ENHANCER = "dialogue_enhancer"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X = "dts_virtual_x"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X_TBHDX = "dts_virtual_x_tbhdx"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X_LIMITER = "dts_virtual_x_limiter"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X = + "dts_virtual_x_tru_surround_x"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD = + "dts_virtual_x_tru_volume_hd"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY = + "dts_virtual_x_dialog_clarity"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X_DEFINITION = "dts_virtual_x_definition"; + + /** + * @hide + */ + public static final String PARAMETER_DTS_VIRTUAL_X_HEIGHT = "dts_virtual_x_height"; + + private SoundQuality() { } } diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig index 4b832aee49c5..572db97595ea 100644 --- a/media/java/android/media/tv/flags/media_tv.aconfig +++ b/media/java/android/media/tv/flags/media_tv.aconfig @@ -112,3 +112,11 @@ flag { description : "Feature flag to enable APIs for applying picture profiles" bug: "337330263" } + +flag { + name: "hdmi_control_collect_physical_address" + is_exported: true + namespace: "media_tv" + description: "Collect physical address from HDMI-CEC messages in metrics" + bug: "376001043" +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 88c1c434cc0d..ec336d5efee6 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -27,6 +27,7 @@ import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; +import android.hardware.camera2.CameraMetadataInfo; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.CaptureResultExtras; import android.hardware.camera2.impl.PhysicalCaptureResultInfo; @@ -217,7 +218,7 @@ public class CameraBinderTest extends AndroidTestCase { * android.hardware.camera2.CaptureResultExtras) */ @Override - public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras, + public void onResultReceived(CameraMetadataInfo result, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) throws RemoteException { // TODO Auto-generated method stub } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index 3758c515c1a5..7d1e5f821c05 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -33,6 +33,7 @@ import android.graphics.SurfaceTexture; import android.hardware.ICameraService; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraMetadataInfo; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; @@ -141,7 +142,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase { * android.hardware.camera2.impl.CameraMetadataNative, * android.hardware.camera2.CaptureResultExtras) */ - public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras, + public void onResultReceived(CameraMetadataInfo result, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) throws RemoteException { // TODO Auto-generated method stub @@ -186,10 +187,14 @@ public class CameraDeviceBinderTest extends AndroidTestCase { } } - class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataNative> { + class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataInfo> { @Override - public boolean matches(CameraMetadataNative obj) { - return !obj.isEmpty(); + public boolean matches(CameraMetadataInfo obj) { + if (obj.getTag() == CameraMetadataInfo.metadata) { + return !(obj.getMetadata().isEmpty()); + } else { + return (obj.getFmqSize() != 0); + } } } diff --git a/native/android/Android.bp b/native/android/Android.bp index da29c49f9d7b..cd6de5a5c8f0 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -55,6 +55,7 @@ cc_library_shared { "surface_control_input_receiver.cpp", "choreographer.cpp", "configuration.cpp", + "display_luts.cpp", "dynamic_instrumentation_manager.cpp", "hardware_buffer_jni.cpp", "input.cpp", diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp new file mode 100644 index 000000000000..179a32bd1c03 --- /dev/null +++ b/native/android/display_luts.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "DisplayLuts" + +#include <android/display_luts.h> +#include <display_luts_private.h> +#include <utils/Log.h> + +#include <cmath> + +#define ADISPLAYLUTS_BUFFER_LENGTH_LIMIT (100000) + +#define CHECK_NOT_NULL(name) \ + LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument"); + +ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, int32_t dimension, + int32_t key) { + CHECK_NOT_NULL(buffer); + LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT, + "the lut raw buffer length is too big to handle"); + if (dimension != ADISPLAYLUTS_ONE_DIMENSION && dimension != ADISPLAYLUTS_THREE_DIMENSION) { + LOG_ALWAYS_FATAL("the lut dimension is be either 1 or 3"); + } + int32_t size = 0; + if (dimension == ADISPLAYLUTS_THREE_DIMENSION) { + LOG_ALWAYS_FATAL_IF(length % 3 != 0, "the 3d lut raw buffer is not divisible by 3"); + int32_t lengthPerChannel = length / 3; + float sizeForDim = std::cbrt(static_cast<float>(lengthPerChannel)); + LOG_ALWAYS_FATAL_IF(sizeForDim != (int)(sizeForDim), + "the 3d lut buffer length is incorrect"); + size = (int)sizeForDim; + } else { + size = length; + } + LOG_ALWAYS_FATAL_IF(size < 2, "the lut size for each dimension is too small"); + + ADisplayLutsEntry* entry = new ADisplayLutsEntry(); + entry->buffer.data.resize(length); + std::copy(buffer, buffer + length, entry->buffer.data.begin()); + entry->properties = {dimension, size, key}; + + entry->incStrong((void*)ADisplayLutsEntry_createEntry); + return static_cast<ADisplayLutsEntry*>(entry); +} + +void ADisplayLutsEntry_destroy(ADisplayLutsEntry* entry) { + if (entry != NULL) { + entry->decStrong((void*)ADisplayLutsEntry_createEntry); + } +} + +ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) { + CHECK_NOT_NULL(entry); + return static_cast<ADisplayLuts_Dimension>(entry->properties.dimension); +} + +int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) { + CHECK_NOT_NULL(entry); + return entry->properties.size; +} + +ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) { + CHECK_NOT_NULL(entry); + return static_cast<ADisplayLuts_SamplingKey>(entry->properties.samplingKey); +} + +const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) { + CHECK_NOT_NULL(entry); + return entry->buffer.data.data(); +} + +ADisplayLuts* ADisplayLuts_create() { + ADisplayLuts* luts = new ADisplayLuts(); + if (luts == NULL) { + delete luts; + return NULL; + } + luts->incStrong((void*)ADisplayLuts_create); + return static_cast<ADisplayLuts*>(luts); +} + +void ADisplayLuts_clearLuts(ADisplayLuts* luts) { + for (auto& entry : luts->entries) { + entry->decStrong((void*)ADisplayLuts_setEntries); // Decrement ref count + } + luts->entries.clear(); + luts->offsets.clear(); + luts->totalBufferSize = 0; +} + +void ADisplayLuts_destroy(ADisplayLuts* luts) { + if (luts != NULL) { + ADisplayLuts_clearLuts(luts); + luts->decStrong((void*)ADisplayLuts_create); + } +} + +void ADisplayLuts_setEntries(ADisplayLuts* luts, ADisplayLutsEntry** entries, int32_t numEntries) { + CHECK_NOT_NULL(luts); + // always clear the previously set lut(s) + ADisplayLuts_clearLuts(luts); + + // do nothing + if (!entries || numEntries == 0) { + return; + } + + LOG_ALWAYS_FATAL_IF(numEntries > 2, "The number of entries should be not over 2!"); + if (numEntries == 2 && entries[0]->properties.dimension != ADISPLAYLUTS_ONE_DIMENSION && + entries[1]->properties.dimension != ADISPLAYLUTS_THREE_DIMENSION) { + LOG_ALWAYS_FATAL("The entries should be 1D and 3D in order!"); + } + + luts->offsets.reserve(numEntries); + luts->entries.reserve(numEntries); + for (int32_t i = 0; i < numEntries; i++) { + luts->offsets.emplace_back(luts->totalBufferSize); + luts->totalBufferSize += entries[i]->buffer.data.size(); + luts->entries.emplace_back(entries[i]); + luts->entries.back()->incStrong((void*)ADisplayLuts_setEntries); + } +}
\ No newline at end of file diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 9da7becba332..7f555a868615 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -95,6 +95,15 @@ LIBANDROID { AConfiguration_setTouchscreen; AConfiguration_setUiModeNight; AConfiguration_setUiModeType; + ADisplayLuts_create; # introduced=36 + ADisplayLuts_setEntries; # introduced=36 + ADisplayLuts_destroy; # introduced=36 + ADisplayLutsEntry_createEntry; # introduced=36 + ADisplayLutsEntry_getDimension; # introduced=36 + ADisplayLutsEntry_getSize; # introduced=36 + ADisplayLutsEntry_getSamplingKey; # introduced=36 + ADisplayLutsEntry_getBuffer; # introduced=36 + ADisplayLutsEntry_destroy; # introduced=36 AInputEvent_getDeviceId; AInputEvent_getSource; AInputEvent_getType; @@ -300,6 +309,7 @@ LIBANDROID { ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29 ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream + ASurfaceTransaction_setLuts; # introduced=36 ASurfaceTransaction_setOnComplete; # introduced=29 ASurfaceTransaction_setOnCommit; # introduced=31 ASurfaceTransaction_setPosition; # introduced=31 diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 698bc84a78b9..fc64e9b48f6d 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -14,12 +14,15 @@ * limitations under the License. */ +#include <android/gui/LutProperties.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/native_window.h> #include <android/surface_control.h> #include <android/surface_control_jni.h> #include <android_runtime/android_view_SurfaceControl.h> #include <configstore/Utils.h> +#include <cutils/ashmem.h> +#include <display_luts_private.h> #include <gui/HdrMetadata.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> @@ -53,6 +56,14 @@ static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPA static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ)); +static_assert(static_cast<int>(ADISPLAYLUTS_ONE_DIMENSION) == + static_cast<int>(android::gui::LutProperties::Dimension::ONE_D)); +static_assert(static_cast<int>(ADISPLAYLUTS_THREE_DIMENSION) == + static_cast<int>(android::gui::LutProperties::Dimension::THREE_D)); +static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_RGB) == + static_cast<int>(android::gui::LutProperties::SamplingKey::RGB)); +static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) == + static_cast<int>(android::gui::LutProperties::SamplingKey::MAX_RGB)); Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) { return reinterpret_cast<Transaction*>(aSurfaceTransaction); @@ -693,6 +704,58 @@ void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* aSurfaceTran transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio); } +void ASurfaceTransaction_setLuts(ASurfaceTransaction* aSurfaceTransaction, + ASurfaceControl* aSurfaceControl, + const struct ADisplayLuts* luts) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + int fd = -1; + std::vector<int32_t> offsets; + std::vector<int32_t> dimensions; + std::vector<int32_t> sizes; + std::vector<int32_t> samplingKeys; + + if (luts) { + std::vector<float> buffer(luts->totalBufferSize); + int32_t count = luts->offsets.size(); + offsets = luts->offsets; + + dimensions.reserve(count); + sizes.reserve(count); + samplingKeys.reserve(count); + for (int32_t i = 0; i < count; i++) { + dimensions.emplace_back(luts->entries[i]->properties.dimension); + sizes.emplace_back(luts->entries[i]->properties.size); + samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey); + std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(), + buffer.begin() + offsets[i]); + } + + // mmap + fd = ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float)); + if (fd < 0) { + LOG_ALWAYS_FATAL("setLuts, ashmem_create_region() failed"); + return; + } + void* ptr = mmap(nullptr, luts->totalBufferSize * sizeof(float), PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + LOG_ALWAYS_FATAL("setLuts, Failed to map the shared memory"); + return; + } + + memcpy(ptr, buffer.data(), luts->totalBufferSize * sizeof(float)); + munmap(ptr, luts->totalBufferSize * sizeof(float)); + } + + transaction->setLuts(surfaceControl, base::unique_fd(fd), offsets, dimensions, sizes, + samplingKeys); +} + void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl, float r, float g, float b, float alpha, diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index c5fb80840b84..b00658052bde 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -73,13 +73,13 @@ public: MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override)); MOCK_METHOD(ScopedAStatus, getCpuHeadroom, (const ::aidl::android::os::CpuHeadroomParamsInternal& in_params, - std::vector<float>* _aidl_return), + std::optional<hal::CpuHeadroomResult>* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, getGpuHeadroom, (const ::aidl::android::os::GpuHeadroomParamsInternal& in_params, - float* _aidl_return), + std::optional<hal::GpuHeadroomResult>* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return), (override)); diff --git a/nfc/tests/src/android/nfc/NdefMessageTest.java b/nfc/tests/src/android/nfc/NdefMessageTest.java new file mode 100644 index 000000000000..9ca295da75c3 --- /dev/null +++ b/nfc/tests/src/android/nfc/NdefMessageTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class NdefMessageTest { + private NdefMessage mNdefMessage; + private NdefRecord mNdefRecord; + + @Before + public void setUp() { + mNdefRecord = NdefRecord.createUri("http://www.example.com"); + mNdefMessage = new NdefMessage(mNdefRecord); + } + + @After + public void tearDown() { + } + + @Test + public void testGetRecords() { + NdefRecord[] records = mNdefMessage.getRecords(); + assertThat(records).isNotNull(); + assertThat(records).hasLength(1); + assertThat(records[0]).isEqualTo(mNdefRecord); + } + + @Test + public void testToByteArray() throws FormatException { + byte[] bytes = mNdefMessage.toByteArray(); + assertThat(bytes).isNotNull(); + assertThat(bytes.length).isGreaterThan(0); + NdefMessage ndefMessage = new NdefMessage(bytes); + assertThat(ndefMessage).isNotNull(); + } +} diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml index 1561e790ff2e..ec6d3fcc185d 100644 --- a/packages/PackageInstaller/res/values-it/strings.xml +++ b/packages/PackageInstaller/res/values-it/strings.xml @@ -43,7 +43,7 @@ <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"L\'amministratore non consente l\'installazione di app ottenute da origini sconosciute"</string> <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Questo utente non può installare app sconosciute"</string> <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non dispone dell\'autorizzazione a installare app"</string> - <string name="ok" msgid="7871959885003339302">"OK"</string> + <string name="ok" msgid="7871959885003339302">"Ok"</string> <string name="archive" msgid="4447791830199354721">"Archivia"</string> <string name="update_anyway" msgid="8792432341346261969">"Aggiorna comunque"</string> <string name="manage_applications" msgid="5400164782453975580">"Gestisci app"</string> diff --git a/packages/SettingsLib/AvatarPicker/res/values-af/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-af/strings.xml index 38cae307f5dc..7209cc2e86af 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-af/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-af/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Kies \'n prent"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Neem \'n foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Kies ’n profielprent"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Verstekgebruikerikoon"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Klaar"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-am/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-am/strings.xml index 917a2fdc6a61..713e0d951a39 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-am/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-am/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"ምስል ይምረጡ"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ፎቶ ያንሱ"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"የመገለጫ ሥዕል ይምረጡ"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ነባሪ የተጠቃሚ አዶ"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"ተከናውኗል"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ar/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ar/strings.xml index 8e5231ba1e77..1b5dbc0248cc 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ar/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ar/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"اختيار صورة"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"التقاط صورة"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"اختيار صورة للملف الشخصي"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"رمز المستخدم التلقائي"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"تم"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-as/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-as/strings.xml index c77b573b26c6..628132809831 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-as/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-as/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"এখন ফট’ তোলক"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"এখন প্ৰ’ফাইল চিত্ৰ বাছনি কৰক"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ডিফ’ল্ট ব্যৱহাৰকাৰীৰ চিহ্ন"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"কৰা হ’ল"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-az/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-az/strings.xml index 6871473a82a3..d47ffceae5d8 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-az/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-az/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Şəkil seçin"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Foto çəkin"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Profil şəkli seçin"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Defolt istifadəçi ikonası"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Hazırdır"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-b+sr+Latn/strings.xml index 42fb478f5ca0..886bd5d946dd 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-b+sr+Latn/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Odaberite sliku"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Slikajte"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Odaberite sliku profila"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Podrazumevana ikona korisnika"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Gotovo"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-be/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-be/strings.xml index c2ef4db67c5d..38a1340d17d5 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-be/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-be/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Выбраць відарыс"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Зрабіць фота"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Выберыце відарыс профілю"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Стандартны карыстальніцкі значок"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Гатова"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-bg/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-bg/strings.xml index eeca7d8d5c9e..90e78b5051c2 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-bg/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-bg/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Избиране на изображение"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Правене на снимка"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Изберете снимка на потребителския профил"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Икона за основния потребител"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Готово"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-bn/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-bn/strings.xml index 6d4d29f47ac6..c56c9dae178c 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-bn/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-bn/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"একটি ছবি বেছে নিন"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ফটো তুলুন"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"কোনও একটি প্রোফাইল ছবি বেছে নিন"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ডিফল্ট ব্যবহারকারীর আইকন"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"হয়ে গেছে"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-bs/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-bs/strings.xml index def1b4525c40..1dd707dd5f13 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-bs/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-bs/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Odaberite sliku"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Snimite fotografiju"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Odaberite sliku profila"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Zadana ikona korisnika"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Gotovo"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ca/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ca/strings.xml index 1b613cafa4df..d8d3171a7b94 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ca/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ca/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Tria una imatge"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Fes una foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Tria una foto de perfil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Icona d\'usuari predeterminat"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Fet"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-cs/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-cs/strings.xml index ffe2f47df489..6ee5b3e56f66 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-cs/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-cs/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Zvolit obrázek"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Vyfotit"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Vyberte profilový obrázek"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Výchozí uživatelská ikona"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Hotovo"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-da/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-da/strings.xml index ab01eef4ab6e..0583399e3ef7 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-da/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-da/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Vælg et billede"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Tag et billede"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Vælg et profilbillede"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon for standardbruger"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Udfør"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-de/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-de/strings.xml index 4d6c651adabd..345b4faff396 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-de/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-de/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Bild auswählen"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Foto aufnehmen"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Profilbild auswählen"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Standardmäßiges Nutzersymbol"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Fertig"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-el/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-el/strings.xml index 9e6813fff4d0..7cecd45dc11f 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-el/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-el/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Επιλέξτε μια εικόνα"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Λήψη φωτογραφίας"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Επιλογή φωτογραφίας προφίλ"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Προεπιλεγμένο εικονίδιο χρήστη"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Τέλος"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-en-rAU/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-en-rAU/strings.xml index d0aae7917ffd..f9905d0eb796 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-en-rAU/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Choose an image"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Take a photo"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Default user icon"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Done"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-en-rGB/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-en-rGB/strings.xml index d0aae7917ffd..f9905d0eb796 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-en-rGB/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Choose an image"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Take a photo"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Default user icon"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Done"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-en-rIN/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-en-rIN/strings.xml index d0aae7917ffd..f9905d0eb796 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-en-rIN/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Choose an image"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Take a photo"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Default user icon"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Done"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-es-rUS/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-es-rUS/strings.xml index 296dcc8603e6..8e9d407bd994 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-es-rUS/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Elegir una imagen"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Tomar una foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Elige una foto de perfil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ícono de usuario predeterminado"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Listo"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-es/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-es/strings.xml index 5d4a8d280796..a7da134da528 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-es/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-es/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Seleccionar una imagen"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Hacer una foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Elige una imagen de perfil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Icono de usuario predeterminado"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Hecho"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-et/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-et/strings.xml index ecd3a83ea2b2..ecf140da20be 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-et/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-et/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Vali pilt"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Pildista"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Profiilipildi valimine"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Vaikekasutajaikoon"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Valmis"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-eu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-eu/strings.xml index e29bc114b5ec..c55d559738a2 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-eu/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-eu/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Aukeratu irudi bat"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Atera argazki bat"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Aukeratu profileko argazki bat"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Erabiltzaile lehenetsiaren ikonoa"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Eginda"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-fa/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fa/strings.xml index 25efac4ba93d..7db55cfa5fc7 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-fa/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-fa/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"انتخاب تصویر"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"عکس گرفتن"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"انتخاب عکس نمایه"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"نماد کاربر پیشفرض"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"تمام"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-fi/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fi/strings.xml index 4d805ed242cb..7a0cca10c5b4 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-fi/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-fi/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Valitse kuva"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Ota kuva"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Valitse profiilikuva"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Oletuskäyttäjäkuvake"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Valmis"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-fr-rCA/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fr-rCA/strings.xml index fb32c5b118fb..e43947111fa9 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-fr-rCA/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Sélectionner une image"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Prendre une photo"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Choisir une photo de profil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Icône d\'utilisateur par défaut"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Terminé"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-fr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-fr/strings.xml index 79620e96bcac..2ffeb4365e36 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-fr/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-fr/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Choisir une image"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Prendre une photo"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Choisir une photo de profil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Icône de l\'utilisateur par défaut"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"OK"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-gl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-gl/strings.xml index bbfa18bf0080..5e46474eb195 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-gl/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-gl/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Escoller unha imaxe"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Tirar unha foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Escoller unha imaxe do perfil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Icona do usuario predeterminado"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Feito"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-gu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-gu/strings.xml index 247afe13d0fb..96e67530cf90 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-gu/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-gu/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"છબી પસંદ કરો"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ફોટો લો"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"પ્રોફાઇલ ફોટો પસંદ કરો"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ડિફૉલ્ટ વપરાશકર્તાનું આઇકન"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"થઈ ગયું"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-hi/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-hi/strings.xml index 3c515769630e..0878ff22f20e 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-hi/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-hi/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"इमेज चुनें"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"फ़ोटो खींचें"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"प्रोफ़ाइल फ़ोटो चुनें"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"उपयोगकर्ता के लिए डिफ़ॉल्ट आइकॉन"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"हो गया"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-hr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-hr/strings.xml index e9a56bdc839e..b599ddf42977 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-hr/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-hr/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Odaberite sliku"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Snimi fotografiju"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Odaberite profilnu sliku"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ikona zadanog korisnika"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Gotovo"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-hu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-hu/strings.xml index f3d53cd57d53..55f4a6098f96 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-hu/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-hu/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Kép kiválasztása"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Fotó készítése"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Profilkép választása"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Alapértelmezett felhasználó ikonja"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Kész"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-in/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-in/strings.xml index bfa0d935c6e7..9b5cbf3717de 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-in/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-in/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Pilih gambar"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Ambil foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Pilih foto profil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon pengguna default"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Selesai"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-is/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-is/strings.xml index d49652480db5..4007c385c8a4 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-is/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-is/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Velja mynd"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Taka mynd"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Veldu prófílmynd"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Tákn sjálfgefins notanda"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Lokið"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-it/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-it/strings.xml index 3ba0a01cbed2..caeb93b79204 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-it/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-it/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Scegli un\'immagine"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Scatta una foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Scegli un\'immagine del profilo"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Icona dell\'utente predefinito"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Fine"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-iw/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-iw/strings.xml index 903989d999e0..03ca68eec0be 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-iw/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-iw/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"לבחירת תמונה"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"צילום תמונה"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"בחירה של תמונת הפרופיל"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"סמל המשתמש שמוגדר כברירת מחדל"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"סיום"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ka/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ka/strings.xml index 817180bb6493..ae61afcba50d 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ka/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ka/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"სურათის არჩევა"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ფოტოს გადაღება"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"პროფილის სურათის არჩევა"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"მომხმარებლის ნაგულისხმევი ხატულა"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"მზადაა"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-kk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-kk/strings.xml index ec01801bace9..f1e8950cd1e7 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-kk/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-kk/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Кескін таңдау"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Суретке түсіру"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Профиль суретін таңдау"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Әдепкі пайдаланушы белгішесі"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Дайын"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-km/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-km/strings.xml index 7a311224646e..a86491e3bb7d 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-km/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-km/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"ជ្រើសរើសរូបភាព"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ថតរូប"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"ជ្រើសរើសរូបភាពកម្រងព័ត៌មាន"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"រូបអ្នកប្រើប្រាស់លំនាំដើម"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"រួចរាល់"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ko/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ko/strings.xml index 45bfb532e6e1..682ebfd827ac 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ko/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ko/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"이미지 선택"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"사진 찍기"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"프로필 사진 선택"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"기본 사용자 아이콘"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"완료"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ky/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ky/strings.xml index b3d39de49bda..3d8e7bff7396 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ky/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ky/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Сүрөт тандоо"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Сүрөткө тартуу"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Профилдин сүрөтүн тандоо"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Демейки колдонуучунун сүрөтчөсү"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Бүттү"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-lo/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-lo/strings.xml index 3ba6709d04a4..64bd871481d5 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-lo/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-lo/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"ເລືອກຮູບ"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ຖ່າຍຮູບ"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"ເລືອກຮູບໂປຣໄຟລ໌"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ໄອຄອນຜູ້ໃຊ້ເລີ່ມຕົ້ນ"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"ແລ້ວໆ"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-lt/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-lt/strings.xml index b0c0c39495f5..4bc183b5cd4c 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-lt/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-lt/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Pasirinkti vaizdą"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Fotografuoti"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Profilio nuotraukos pasirinkimas"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Numatytojo naudotojo piktograma"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Atlikta"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-lv/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-lv/strings.xml index e664c80b5e7c..72e6ec438389 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-lv/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-lv/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Izvēlēties attēlu"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Uzņemt fotoattēlu"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Profila attēla izvēle"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Noklusējuma lietotāja ikona"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Gatavs"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-mk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-mk/strings.xml index bb0e2476bdc2..4b6939ec7775 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-mk/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-mk/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Изберете слика"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Фотографирај"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Изберете профилна слика"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Икона за стандарден корисник"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Готово"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ml/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ml/strings.xml index 674b4f5d8fb5..aec39b13074c 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ml/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ml/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ഒരു ഫോട്ടോ എടുക്കുക"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"പ്രൊഫൈൽ ചിത്രം തിരഞ്ഞെടുക്കുക"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ഡിഫോൾട്ട് ഉപയോക്തൃ ഐക്കൺ"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"പൂർത്തിയായി"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-mn/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-mn/strings.xml index 61553b54d745..4d13e871aa64 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-mn/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-mn/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Зураг сонгох"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Зураг авах"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Профайл зураг сонгох"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Өгөгдмөл хэрэглэгчийн дүрс тэмдэг"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Болсон"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ms/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ms/strings.xml index 25996969e61e..45f1b36e9332 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ms/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ms/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Pilih imej"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Ambil foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Pilih gambar profil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon pengguna lalai"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Selesai"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-my/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-my/strings.xml index f9f697b3c200..697308460b15 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-my/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-my/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"ပုံရွေးရန်"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ဓာတ်ပုံရိုက်ရန်"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"ပရိုဖိုင်ပုံ ရွေးပါ"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"မူရင်းအသုံးပြုသူ သင်္ကေတ"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"ပြီးပြီ"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-nb/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-nb/strings.xml index c1b7631fa8b2..8a5500665a57 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-nb/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-nb/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Velg et bilde"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Ta et bilde"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Velg et profilbilde"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Standard brukerikon"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Ferdig"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ne/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ne/strings.xml index a8c02b97cac8..8373058d7fd5 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ne/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ne/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"कुनै फोटो छनौट गर्नुहोस्"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"फोटो खिच्नुहोस्"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"प्रोफाइल फोटो छान्नुहोस्"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"प्रयोगकर्ताको डिफल्ट आइकन"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"सम्पन्न भयो"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-nl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-nl/strings.xml index 47352bc12100..cb3bd51cb9d4 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-nl/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-nl/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Een afbeelding kiezen"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Een foto maken"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Kies een profielfoto"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Standaard gebruikersicoon"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Klaar"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-or/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-or/strings.xml index 132b97a6f46c..dd54ac304a24 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-or/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-or/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"ଗୋଟିଏ ଛବି ବାଛନ୍ତୁ"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"ଏକ ପ୍ରୋଫାଇଲ ଛବି ବାଛନ୍ତୁ"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ଡିଫଲ୍ଟ ୟୁଜର ଆଇକନ"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"ହୋଇଗଲା"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-pl/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pl/strings.xml index 7db79047047b..35787c91d141 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-pl/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-pl/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Wybierz obraz"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Zrób zdjęcie"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Wybierz zdjęcie profilowe"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ikona domyślnego użytkownika"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Gotowe"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-pt-rBR/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pt-rBR/strings.xml index ae3e6e508c8b..ba1ce2ad34e1 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-pt-rBR/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Escolher uma imagem"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Tirar uma foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Escolha uma foto de perfil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ícone de usuário padrão"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Concluir"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-pt/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-pt/strings.xml index ae3e6e508c8b..ba1ce2ad34e1 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-pt/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-pt/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Escolher uma imagem"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Tirar uma foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Escolha uma foto de perfil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ícone de usuário padrão"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Concluir"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ro/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ro/strings.xml index ce662d41a113..51bcd5794cb9 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ro/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ro/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Alege o imagine"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Fă o fotografie"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Alege o fotografie de profil"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Pictograma prestabilită a utilizatorului"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Gata"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-ru/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-ru/strings.xml index 47f8a839beb4..59fb913841e8 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-ru/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-ru/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Выбрать фото"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Сделать снимок"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Выберите фото профиля"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Значок пользователя по умолчанию"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Готово"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-si/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-si/strings.xml index aaba44266bb1..db7e0b8e4b65 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-si/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-si/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"රූපයක් තෝරන්න"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ඡායාරූපයක් ගන්න"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"පැතිකඩ පින්තූරයක් තේරීම"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"පෙරනිමි පරිශීලක නිරූපකය"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"නිමයි"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-sk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sk/strings.xml index 3f801a36843d..36e94f3c5ed6 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-sk/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-sk/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Vybrať obrázok"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Odfotiť"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Vyberte profilovú fotku"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Predvolená ikona používateľa"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Hotovo"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-sq/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sq/strings.xml index 0b8a58d0d5d5..0093380052db 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-sq/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-sq/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Zgjidh një imazh"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Bëj një fotografi"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Zgjidh një fotografi profili"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ikona e parazgjedhur e përdoruesit"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"U krye"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-sr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sr/strings.xml index 1014df4f3b95..971d8c567cdc 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-sr/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-sr/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Одаберите слику"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Сликајте"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Одаберите слику профила"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Подразумевана икона корисника"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Готово"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-sv/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sv/strings.xml index 82034092288a..9196e2ac2adc 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-sv/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-sv/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Välj en bild"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Ta ett foto"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Välj en profilbild"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Ikon för standardanvändare"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Klar"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-sw/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-sw/strings.xml index b0e8c44d5e8a..a0bc5548f6fe 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-sw/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-sw/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Chagua picha"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Piga picha"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Chagua picha ya wasifu"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Aikoni chaguomsingi ya mtumiaji"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Nimemaliza"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-th/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-th/strings.xml index 31e753e04cc1..b8b4324878af 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-th/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-th/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"เลือกรูปภาพ"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"ถ่ายรูป"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"เลือกรูปโปรไฟล์"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"ไอคอนผู้ใช้เริ่มต้น"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"เสร็จสิ้น"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-tr/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-tr/strings.xml index 26d96c22c7f2..3e10257d6b24 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-tr/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-tr/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Resim seç"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Fotoğraf çek"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Profil fotoğrafı seçin"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Varsayılan kullanıcı simgesi"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Bitti"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-uk/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-uk/strings.xml index 1fbe5fb1076a..ee4a2fc22960 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-uk/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-uk/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Вибрати зображення"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Зробити фото"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Виберіть зображення профілю"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Значок користувача за умовчанням"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Готово"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-vi/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-vi/strings.xml index 036c0065ebd7..c6b4ef5ef050 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-vi/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-vi/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Chọn hình ảnh"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Chụp ảnh"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Chọn một ảnh hồ sơ"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Biểu tượng người dùng mặc định"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Xong"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-zh-rCN/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zh-rCN/strings.xml index 58488b35b0a6..684449f446e1 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-zh-rCN/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"选择图片"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"拍照"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"选择个人资料照片"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"默认用户图标"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"完成"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-zh-rHK/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zh-rHK/strings.xml index e5f375205844..e0d405272dc8 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-zh-rHK/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"選擇圖片"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"拍攝相片"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"選擇個人檔案相片"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"預設使用者圖示"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"完成"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-zh-rTW/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zh-rTW/strings.xml index 4a58180ba15f..d82ad7e43417 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-zh-rTW/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"選擇圖片"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"拍照"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"選擇個人資料相片"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"預設使用者圖示"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"完成"</string> </resources> diff --git a/packages/SettingsLib/AvatarPicker/res/values-zu/strings.xml b/packages/SettingsLib/AvatarPicker/res/values-zu/strings.xml index ad15f890cd05..8678332d8b54 100644 --- a/packages/SettingsLib/AvatarPicker/res/values-zu/strings.xml +++ b/packages/SettingsLib/AvatarPicker/res/values-zu/strings.xml @@ -19,9 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="user_image_choose_photo" msgid="5630717762469961028">"Khetha isithombe"</string> <string name="user_image_take_photo" msgid="3147097821937166738">"Thatha isithombe"</string> - <!-- no translation found for avatar_picker_title (7478146965334560463) --> - <skip /> + <string name="avatar_picker_title" msgid="7478146965334560463">"Khetha isithombe sephrofayela"</string> <string name="default_user_icon_description" msgid="6018582161341388812">"Isithonjana somsebenzisi sokuzenzakalelayo"</string> - <!-- no translation found for done (3587741621903511576) --> - <skip /> + <string name="done" msgid="3587741621903511576">"Kwenziwe"</string> </resources> diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt index c2728b4ad012..7601b9a31041 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt @@ -20,7 +20,7 @@ import androidx.preference.PreferenceDataStore import com.android.settingslib.datastore.KeyValueStore /** Adapter to translate [KeyValueStore] into [PreferenceDataStore]. */ -class PreferenceDataStoreAdapter(private val keyValueStore: KeyValueStore) : PreferenceDataStore() { +class PreferenceDataStoreAdapter(val keyValueStore: KeyValueStore) : PreferenceDataStore() { override fun getBoolean(key: String, defValue: Boolean): Boolean = keyValueStore.getValue(key, Boolean::class.javaObjectType) ?: defValue diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt index 62ac3ade62a4..7cec59c9090c 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt @@ -74,16 +74,12 @@ class PreferenceScreenBindingHelper( private val preferences: ImmutableMap<String, PreferenceHierarchyNode> private val dependencies: ImmutableMultimap<String, String> private val lifecycleAwarePreferences: Array<PreferenceLifecycleProvider> - private val storages = mutableSetOf<KeyedObservable<String>>() + private val storages = mutableMapOf<String, KeyedObservable<String>>() private val preferenceObserver: KeyedObserver<String?> private val storageObserver = - KeyedObserver<String?> { key, _ -> - if (key != null) { - notifyChange(key, CHANGE_REASON_VALUE) - } - } + KeyedObserver<String> { key, _ -> notifyChange(key, CHANGE_REASON_VALUE) } init { val preferencesBuilder = ImmutableMap.builder<String, PreferenceHierarchyNode>() @@ -98,7 +94,6 @@ class PreferenceScreenBindingHelper( preferencesBuilder.put(it.key, this) it.dependencyOfEnabledState(context)?.addDependency(it) if (it is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(it) - if (it is PersistentPreference<*>) storages.add(it.storage(context)) } } @@ -120,7 +115,16 @@ class PreferenceScreenBindingHelper( preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) } addObserver(preferenceObserver, mainExecutor) - for (storage in storages) storage.addObserver(storageObserver, mainExecutor) + + preferenceScreen.forEachRecursively { + val preferenceDataStore = it.preferenceDataStore + if (preferenceDataStore is PreferenceDataStoreAdapter) { + val key = it.key + val keyValueStore = preferenceDataStore.keyValueStore + storages[key] = keyValueStore + keyValueStore.addObserver(key, storageObserver, mainExecutor) + } + } } private fun onPreferenceChange(key: String?, reason: Int) { @@ -181,7 +185,7 @@ class PreferenceScreenBindingHelper( fun onDestroy() { removeObserver(preferenceObserver) - for (storage in storages) storage.removeObserver(storageObserver) + for ((key, storage) in storages) storage.removeObserver(key, storageObserver) for (preference in lifecycleAwarePreferences) { preference.onDestroy(preferenceLifecycleContext) } diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt new file mode 100644 index 000000000000..2e7221bd4d7f --- /dev/null +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.preference + +import androidx.preference.Preference +import androidx.preference.PreferenceGroup + +/** Traversals preference hierarchy recursively and applies an action. */ +fun PreferenceGroup.forEachRecursively(action: (Preference) -> Unit) { + action.invoke(this) + for (index in 0 until preferenceCount) { + val preference = getPreference(index) + if (preference is PreferenceGroup) { + preference.forEachRecursively(action) + } else { + action.invoke(preference) + } + } +} diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml index b5534b9c9246..6de84388bbe4 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml @@ -18,5 +18,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="enabled_by_admin" msgid="6630472777476410137">"एडमिन की ओर से चालू किया गया"</string> - <string name="disabled_by_admin" msgid="4023569940620832713">"एडमिन ने यह सुविधा बंद की है"</string> + <string name="disabled_by_admin" msgid="4023569940620832713">"एडमिन ने यह सुविधा बंद की हुई है"</string> </resources> diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml index 2ab8142d7610..ecfa2c794e28 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml @@ -18,5 +18,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="enabled_by_admin" msgid="6630472777476410137">"A rendszergazda bekapcsolta"</string> - <string name="disabled_by_admin" msgid="4023569940620832713">"A rendszergazda kikapcsolta"</string> + <string name="disabled_by_admin" msgid="4023569940620832713">"A rendszergazda letiltotta"</string> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-af/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-af/strings.xml new file mode 100644 index 000000000000..eb48b4ec2515 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-af/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Vou uit"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Vou in"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-am/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-am/strings.xml new file mode 100644 index 000000000000..442819244b25 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-am/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ዘርጋ"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ሰብስብ"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml new file mode 100644 index 000000000000..e6d4c7b6cd19 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"توسيع"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"تصغير"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-as/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-as/strings.xml new file mode 100644 index 000000000000..2b5a5c98b98c --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-as/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"বিস্তাৰ কৰক"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"সংকোচন কৰক"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-az/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-az/strings.xml new file mode 100644 index 000000000000..c7adee3095f2 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-az/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Genişləndirin"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Yığcamlaşdırın"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..8b245a63cd91 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Proširi"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Skupi"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-be/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-be/strings.xml new file mode 100644 index 000000000000..b468f819397a --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-be/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Разгарнуць"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Згарнуць"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml new file mode 100644 index 000000000000..b177fa76a7d9 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Разгъване"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Свиване"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml new file mode 100644 index 000000000000..67bb59fab828 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"বড় করুন"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"আড়াল করুন"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml new file mode 100644 index 000000000000..31afc8b07002 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Proširi"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Suzi"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml new file mode 100644 index 000000000000..0f999c9a20e2 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Desplega"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Replega"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml new file mode 100644 index 000000000000..144dba82e50b --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Rozbalit"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Sbalit"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-da/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-da/strings.xml new file mode 100644 index 000000000000..85497f143ef9 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-da/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Udvid"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Skjul"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-de/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-de/strings.xml new file mode 100644 index 000000000000..9e47741a0945 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-de/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Maximieren"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Minimieren"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-el/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-el/strings.xml new file mode 100644 index 000000000000..0b325b5ac4cd --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-el/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Ανάπτυξη"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Σύμπτυξη"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml new file mode 100644 index 000000000000..2539aa040a60 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expand"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Collapse"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml new file mode 100644 index 000000000000..2539aa040a60 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expand"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Collapse"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml new file mode 100644 index 000000000000..2539aa040a60 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expand"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Collapse"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml new file mode 100644 index 000000000000..c976a2ed7757 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expandir"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Contraer"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-es/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-es/strings.xml new file mode 100644 index 000000000000..72ba9d11ecf1 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-es/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Mostrar"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Ocultar"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-et/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-et/strings.xml new file mode 100644 index 000000000000..856360669354 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-et/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Laienda"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Ahenda"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml new file mode 100644 index 000000000000..d8c2c35aed31 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Zabaldu"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Tolestu"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml new file mode 100644 index 000000000000..24087411b5b9 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ازهم بازکردن"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"جمع کردن"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml new file mode 100644 index 000000000000..0d226bf8bbe6 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Laajenna"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Tiivistä"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml new file mode 100644 index 000000000000..fecfaa275e5e --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Développer"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Réduire"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml new file mode 100644 index 000000000000..fecfaa275e5e --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Développer"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Réduire"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml new file mode 100644 index 000000000000..7999aa1b4658 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Despregar"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Contraer"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml new file mode 100644 index 000000000000..1457d34d3691 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"મોટું કરો"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"નાનું કરો"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml new file mode 100644 index 000000000000..856379aaea84 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"बड़ा करें"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"छोटा करें"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-hr/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-hr/strings.xml new file mode 100644 index 000000000000..2d637f4a94c0 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-hr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Proširi"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Sažmi"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml new file mode 100644 index 000000000000..42731635f35c --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Kibontás"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Összecsukás"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-in/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-in/strings.xml new file mode 100644 index 000000000000..97c1d602b94a --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-in/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Luaskan"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Ciutkan"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-is/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-is/strings.xml new file mode 100644 index 000000000000..cc4d05bc869c --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-is/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Stækka"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Minnka"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-it/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-it/strings.xml new file mode 100644 index 000000000000..8edcf2450b22 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-it/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Espandi"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Comprimi"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml new file mode 100644 index 000000000000..784bd8e34789 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"הרחבה"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"כיווץ"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ka/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ka/strings.xml new file mode 100644 index 000000000000..ec8f1ddaad7d --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ka/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"გაფართოება"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ჩაკეცვა"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml new file mode 100644 index 000000000000..329ccbbcddde --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Жаю"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Жию"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-km/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-km/strings.xml new file mode 100644 index 000000000000..5ca0269e8eb8 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-km/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ពង្រីក"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"បង្រួម"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml new file mode 100644 index 000000000000..05d4921064cc --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"펼치기"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"접기"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml new file mode 100644 index 000000000000..4f5b8dc3643d --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Жайып көрсөтүү"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Жыйыштыруу"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-lo/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-lo/strings.xml new file mode 100644 index 000000000000..d6ec47939596 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-lo/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ຂະຫຍາຍ"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ຫຍໍ້ລົງ"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-lt/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-lt/strings.xml new file mode 100644 index 000000000000..54076c4fd3c2 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-lt/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Išskleisti"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Sutraukti"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml new file mode 100644 index 000000000000..2f87b0eb7abe --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Izvērst"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Sakļaut"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml new file mode 100644 index 000000000000..b4f8fb901e26 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Прошири"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Собери"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml new file mode 100644 index 000000000000..c68141e31d96 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"വികസിപ്പിക്കുക"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ചുരുക്കുക"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml new file mode 100644 index 000000000000..86b333d115d0 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Дэлгэх"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Хураах"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ms/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ms/strings.xml new file mode 100644 index 000000000000..1ffa5a083575 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ms/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Kembangkan"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Kuncupkan"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-my/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-my/strings.xml new file mode 100644 index 000000000000..6f79acc6e8ad --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-my/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ပိုပြပါ"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"လျှော့ပြပါ"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml new file mode 100644 index 000000000000..359c9abe316e --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Vis"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Skjul"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml new file mode 100644 index 000000000000..374fd31129c9 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"एक्स्पान्ड गर्नुहोस्"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"कोल्याप्स गर्नुहोस्"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml new file mode 100644 index 000000000000..76a4f9996340 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Uitvouwen"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Samenvouwen"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-or/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-or/strings.xml new file mode 100644 index 000000000000..1e1e87025986 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-or/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ବିସ୍ତାର କରନ୍ତୁ"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-pl/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-pl/strings.xml new file mode 100644 index 000000000000..da273b3bd137 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-pl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Rozwiń"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Zwiń"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-pt-rBR/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-pt-rBR/strings.xml new file mode 100644 index 000000000000..4e3d0e624d16 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-pt-rBR/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Abrir"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Fechar"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-pt/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-pt/strings.xml new file mode 100644 index 000000000000..4e3d0e624d16 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-pt/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Abrir"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Fechar"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml new file mode 100644 index 000000000000..ec208843efa4 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Extinde"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Restrânge"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml new file mode 100644 index 000000000000..ba6ab9687d41 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Развернуть"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Свернуть"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-si/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-si/strings.xml new file mode 100644 index 000000000000..9adb6466c9a4 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-si/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"දිග හරින්න"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"හකුළන්න"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-sk/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sk/strings.xml new file mode 100644 index 000000000000..574ee839a089 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-sk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Rozbaliť"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Zbaliť"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml new file mode 100644 index 000000000000..e02ecbfada36 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Zgjero"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Palos"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-sr/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sr/strings.xml new file mode 100644 index 000000000000..35f6aa3a635a --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-sr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Прошири"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Скупи"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml new file mode 100644 index 000000000000..241683984626 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Utöka"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Komprimera"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml new file mode 100644 index 000000000000..9a6075842a22 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Panua"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Kunja"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-th/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-th/strings.xml new file mode 100644 index 000000000000..d6dce9c076c9 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-th/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ขยาย"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ยุบ"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml new file mode 100644 index 000000000000..8c7dbcfd987a --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Genişlet"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Daralt"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml new file mode 100644 index 000000000000..6da0ca81450e --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Розгорнути"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Згорнути"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml new file mode 100644 index 000000000000..46f3351b7964 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Mở rộng"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Thu gọn"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml new file mode 100644 index 000000000000..ea5f29b2413b --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"展开"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"收起"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml new file mode 100644 index 000000000000..36203139566c --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"展開"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"收合"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml new file mode 100644 index 000000000000..36203139566c --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"展開"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"收合"</string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml new file mode 100644 index 000000000000..725d8bc27b2f --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Nweba"</string> + <string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Goqa"</string> +</resources> diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index 73d0beccd0ba..02e190417853 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath allprojects { extra["androidTop"] = androidTop - extra["jetpackComposeVersion"] = "1.7.3" + extra["jetpackComposeVersion"] = "1.8.0-alpha06" } subprojects { diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml index 272dc2d1958f..74811d3ae7a6 100644 --- a/packages/SettingsLib/Spa/gradle/libs.versions.toml +++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml @@ -15,7 +15,7 @@ # [versions] -agp = "8.6.1" +agp = "8.7.2" compose-compiler = "1.5.11" dexmaker-mockito = "2.28.3" jvm = "17" diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.11.1-bin.zip Binary files differindex 45f0424639f2..f8c4ecb9df7a 100644 --- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip +++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.11.1-bin.zip diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties index 1c25e97452eb..ca510ebb7271 100644 --- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties +++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties @@ -16,6 +16,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=gradle-8.10.2-bin.zip +distributionUrl=gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index 914f06c1fef7..1f32ad6622a2 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -54,14 +54,14 @@ android { dependencies { api(project(":SettingsLibColor")) api("androidx.appcompat:appcompat:1.7.0") - api("androidx.compose.material3:material3:1.4.0-alpha01") - api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion") + api("androidx.compose.material3:material3:1.4.0-alpha04") + api("androidx.compose.material:material-icons-extended") api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion") api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion") api("androidx.graphics:graphics-shapes-android:1.0.1") api("androidx.lifecycle:lifecycle-livedata-ktx") api("androidx.lifecycle:lifecycle-runtime-compose") - api("androidx.navigation:navigation-compose:2.8.1") + api("androidx.navigation:navigation-compose:2.9.0-alpha03") api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha") api("com.google.android.material:material:1.12.0") debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion") diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt index ea6a2720c512..7466f95e3fb8 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt @@ -41,8 +41,6 @@ import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.IPackageManagers import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.toRoute -import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation -import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference @@ -155,12 +153,12 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp override val changeable = { isChangeable } override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) } } - val restrictions = Restrictions(userId = userId, - keys = switchRestrictionKeys, - enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation( - key = it, - packageName = packageName) }) - RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory) + RestrictedSwitchPreference( + model = switchModel, + restrictions = getRestrictions(userId, packageName), + ifBlockedByAdminOverrideCheckedValueTo = switchifBlockedByAdminOverrideCheckedValueTo, + restrictionsProviderFactory = restrictionsProviderFactory, + ) InfoPageAdditionalContent(record, isAllowed) } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt index 627b248e75b9..d2867af1eda6 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt @@ -26,6 +26,8 @@ import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.compose.rememberContext import com.android.settingslib.spa.framework.util.asyncMapItem import com.android.settingslib.spaprivileged.model.app.AppRecord +import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation +import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import kotlinx.coroutines.flow.Flow /** @@ -38,6 +40,16 @@ interface TogglePermissionAppListModel<T : AppRecord> { val switchRestrictionKeys: List<String> get() = emptyList() + /** + * If this is not null, and on a switch UI restricted by admin, the switch's checked status will + * be overridden. + * + * And if there is an admin summary, such as "Enabled by admin" or "Disabled by admin", will + * also be overridden. + */ + val switchifBlockedByAdminOverrideCheckedValueTo: Boolean? + get() = null + val enhancedConfirmationKey: String? get() = null @@ -101,6 +113,16 @@ fun <T : AppRecord> TogglePermissionAppListModel<T>.isChangeableWithSystemUidChe record: T, ): Boolean = !record.isSystemOrRootUid() && isChangeable(record) +fun <T : AppRecord> TogglePermissionAppListModel<T>.getRestrictions( + userId: Int, + packageName: String, +) = + Restrictions( + userId = userId, + keys = switchRestrictionKeys, + enhancedConfirmation = + enhancedConfirmationKey?.let { key -> EnhancedConfirmation(key, packageName) }, + ) interface TogglePermissionAppListProvider { val permissionType: String diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt index 57102ba9ea46..ec44d2af4ffa 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt @@ -42,8 +42,6 @@ import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder import com.android.settingslib.spaprivileged.model.app.AppListModel import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.userId -import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation -import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode @@ -151,23 +149,19 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>( @Composable fun getSummary(record: T): () -> String { - val restrictions = remember(record.app.userId, record.app.packageName) { - Restrictions( + val restrictions = + listModel.getRestrictions( userId = record.app.userId, - keys = listModel.switchRestrictionKeys, - enhancedConfirmation = listModel.enhancedConfirmationKey?.let { - EnhancedConfirmation( - key = it, - packageName = record.app.packageName) - }) - } + packageName = record.app.packageName, + ) val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions) val allowed = listModel.isAllowed(record) return RestrictedSwitchPreferenceModel.getSummary( context = context, - restrictedModeSupplier = { restrictedMode }, summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed()) }, - checked = allowed, + checkedIfNoRestricted = allowed, + checkedIfBlockedByAdmin = listModel.switchifBlockedByAdminOverrideCheckedValueTo, + restrictedModeSupplier = { restrictedMode }, ) } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt index cd720252e485..5fec110351bf 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt @@ -25,12 +25,25 @@ import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvid import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreferenceModel.Companion.RestrictedSwitchWrapper +/** + * @param ifBlockedByAdminOverrideCheckedValueTo if this is not null and there is an admin + * restriction, the switch's checked status will be overridden. + * + * And if there is an admin summary, such as "Enabled by admin" or "Disabled by admin", will also + * be overridden. + */ @Composable fun RestrictedSwitchPreference( model: SwitchPreferenceModel, restrictions: Restrictions, + ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null, ) { - RestrictedSwitchPreference(model, restrictions, ::RestrictionsProviderImpl) + RestrictedSwitchPreference( + model = model, + restrictions = restrictions, + ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo, + restrictionsProviderFactory = ::RestrictionsProviderImpl, + ) } @VisibleForTesting @@ -38,13 +51,18 @@ fun RestrictedSwitchPreference( internal fun RestrictedSwitchPreference( model: SwitchPreferenceModel, restrictions: Restrictions, + ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null, restrictionsProviderFactory: RestrictionsProviderFactory, ) { if (restrictions.isEmpty()) { SwitchPreference(model) return } - restrictionsProviderFactory.RestrictedSwitchWrapper(model, restrictions) { + restrictionsProviderFactory.RestrictedSwitchWrapper( + model = model, + restrictions = restrictions, + ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo, + ) { SwitchPreference(it) } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt index fb23637a9f4c..0bb92ce72595 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt @@ -42,25 +42,28 @@ internal class RestrictedSwitchPreferenceModel( context: Context, model: SwitchPreferenceModel, private val restrictedMode: RestrictedMode?, + private val ifBlockedByAdminOverrideCheckedValueTo: Boolean?, ) : SwitchPreferenceModel { override val title = model.title - override val summary = getSummary( - context = context, - restrictedModeSupplier = { restrictedMode }, - summaryIfNoRestricted = model.summary, - checked = model.checked, - ) + override val checked = + when (restrictedMode) { + null -> ({ null }) + is NoRestricted -> model.checked + is BaseUserRestricted -> ({ false }) + is BlockedByAdmin -> ({ ifBlockedByAdminOverrideCheckedValueTo ?: model.checked() }) + is BlockedByEcm -> model.checked + } - override val icon = model.icon + override val summary = + getSummary( + context = context, + restrictedModeSupplier = { restrictedMode }, + summaryIfNoRestricted = model.summary, + checkedIfNoRestricted = checked, + ) - override val checked = when (restrictedMode) { - null -> ({ null }) - is NoRestricted -> model.checked - is BaseUserRestricted -> ({ false }) - is BlockedByAdmin -> model.checked - is BlockedByEcm -> model.checked - } + override val icon = model.icon override val changeable = restrictedMode.restrictEnabled(model.changeable) @@ -112,12 +115,20 @@ internal class RestrictedSwitchPreferenceModel( fun RestrictedSwitchWrapper( model: SwitchPreferenceModel, restrictedMode: RestrictedMode?, + ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null, content: @Composable (SwitchPreferenceModel) -> Unit, ) { val context = LocalContext.current - val restrictedSwitchPreferenceModel = remember(restrictedMode, model) { - RestrictedSwitchPreferenceModel(context, model, restrictedMode) - } + val restrictedSwitchPreferenceModel = + remember(restrictedMode, model) { + RestrictedSwitchPreferenceModel( + context = context, + model = model, + restrictedMode = restrictedMode, + ifBlockedByAdminOverrideCheckedValueTo = + ifBlockedByAdminOverrideCheckedValueTo, + ) + } restrictedSwitchPreferenceModel.RestrictionWrapper { content(restrictedSwitchPreferenceModel) } @@ -127,23 +138,31 @@ internal class RestrictedSwitchPreferenceModel( fun RestrictionsProviderFactory.RestrictedSwitchWrapper( model: SwitchPreferenceModel, restrictions: Restrictions, + ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null, content: @Composable (SwitchPreferenceModel) -> Unit, ) { - RestrictedSwitchWrapper(model, rememberRestrictedMode(restrictions).value, content) + RestrictedSwitchWrapper( + model = model, + restrictedMode = rememberRestrictedMode(restrictions).value, + ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo, + content = content, + ) } fun getSummary( context: Context, - restrictedModeSupplier: () -> RestrictedMode?, summaryIfNoRestricted: () -> String, - checked: () -> Boolean?, + checkedIfNoRestricted: () -> Boolean?, + checkedIfBlockedByAdmin: Boolean? = null, + restrictedModeSupplier: () -> RestrictedMode?, ): () -> String = { when (val restrictedMode = restrictedModeSupplier()) { is NoRestricted -> summaryIfNoRestricted() is BaseUserRestricted -> context.getString(com.android.settingslib.R.string.disabled) - is BlockedByAdmin -> restrictedMode.getSummary(checked()) + is BlockedByAdmin -> + restrictedMode.getSummary(checkedIfBlockedByAdmin ?: checkedIfNoRestricted()) is BlockedByEcm -> context.getString(com.android.settingslib.R.string.disabled) diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt index bf0ad0b29072..e73611510f6b 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt @@ -26,9 +26,11 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.spa.testutils.FakeNavControllerWrapper import com.android.settingslib.spaprivileged.R import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder +import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdminImpl import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider import com.android.settingslib.spaprivileged.tests.testutils.TestAppRecord @@ -97,6 +99,26 @@ class TogglePermissionAppListPageTest { } @Test + fun summary_whenAllowedButAdminOverrideToNotAllowed() { + fakeRestrictionsProvider.restrictedMode = + BlockedByAdminImpl(context = context, enforcedAdmin = ENFORCED_ADMIN) + val listModel = + TestTogglePermissionAppListModel( + isAllowed = true, + switchifBlockedByAdminOverrideCheckedValueTo = false, + ) + + val summary = getSummary(listModel) + + assertThat(summary) + .isEqualTo( + context.getString( + com.android.settingslib.widget.restricted.R.string.disabled_by_admin + ) + ) + } + + @Test fun appListItem_onClick_navigate() { val listModel = TestTogglePermissionAppListModel() composeTestRule.setContent { @@ -162,8 +184,9 @@ class TogglePermissionAppListPageTest { const val PACKAGE_NAME = "package.name" const val LABEL = "Label" const val SUMMARY = "Summary" - val APP = ApplicationInfo().apply { - packageName = PACKAGE_NAME - } + val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME } + const val RESTRICTION = "restriction" + val ENFORCED_ADMIN: RestrictedLockUtils.EnforcedAdmin = + RestrictedLockUtils.EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION) } } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt index b88d1c5760a6..1fd7ecf3cf40 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt @@ -121,7 +121,7 @@ class RestrictedSwitchPreferenceTest { } @Test - fun whenBlockedByAdmin_disabled() { + fun whenBlockedByAdmin_notOverrideChecked() { val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY)) fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin @@ -133,6 +133,18 @@ class RestrictedSwitchPreferenceTest { } @Test + fun whenBlockedByAdmin_overrideCheckedToFalse() { + val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY)) + fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin + + setContent(restrictions, ifBlockedByAdminOverrideCheckedValueTo = false) + + composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled() + composeTestRule.onNodeWithText(FakeBlockedByAdmin.SUMMARY).assertIsDisplayed() + composeTestRule.onNode(isOff()).assertIsDisplayed() + } + + @Test fun whenBlockedByAdmin_click() { val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY)) fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin @@ -166,9 +178,16 @@ class RestrictedSwitchPreferenceTest { assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue() } - private fun setContent(restrictions: Restrictions) { + private fun setContent( + restrictions: Restrictions, + ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null, + ) { composeTestRule.setContent { - RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ -> + RestrictedSwitchPreference( + model = switchPreferenceModel, + restrictions = restrictions, + ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo, + ) { _, _ -> fakeRestrictionsProvider } } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt index 17903130a8a5..000743e7ef8a 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.Flow class TestTogglePermissionAppListModel( isAllowed: Boolean? = null, private val isChangeable: Boolean = false, + override val switchifBlockedByAdminOverrideCheckedValueTo: Boolean? = null, ) : TogglePermissionAppListModel<TestAppRecord> { override val pageTitleResId = R.string.test_permission_title override val switchTitleResId = R.string.test_permission_switch_title diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 076f82acce09..89de995fa5ef 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -178,3 +178,10 @@ flag { description: "Enable the input routing control in device details and hearing devices dialog." bug: "349255906" } + +flag { + name: "hearing_device_set_connection_status_report" + namespace: "accessibility" + description: "Enable the connection status report for a set of hearing device." + bug: "357878944" +} diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 322598574f26..656c32d98377 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Hierdie foon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Hierdie tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Hierdie rekenaar (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokluidspreker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Eksterne toestel"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Gekoppel deur ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Gekoppel deur eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV-verstek"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-uitset"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne luidsprekers"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string> <string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 34f37b47fae6..3757d9064500 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ይህ ስልክ"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ይህ ጡባዊ"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ይህ ኮምፒውተር (ውስጣዊ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"የመትከያ ድምፅ ማውጫ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"የውጭ መሣሪያ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI ኢኤአርሲ"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"በኤአርሲ በኩል ተገናኝቷል"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"በኢኤአርሲ በኩል ተገናኝቷል"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"የቲቪ ነባሪ"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"የHDMI ውጤት"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ውስጣዊ ድምፅ ማውጫዎች"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 2ea1486ace31..838b9742c1c7 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"هذا الهاتف"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"هذا الجهاز اللوحي"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"هذا الكمبيوتر (داخلي)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"مكبّر صوت بقاعدة إرساء"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"جهاز خارجي"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"متّصل من خلال ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"متّصل من خلال eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"الجهاز التلقائي لإخراج صوت التلفزيون"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"إخراج الصوت من خلال HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"مكبّرات الصوت الداخلية"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 432d443a5045..d00e7eeef8de 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফ’নটো"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই টেবলেটটো"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"এই কম্পিউটাৰ (অভ্যন্তৰীণ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ড’ক স্পীকাৰ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"বাহ্যিক ডিভাইচ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARCৰ জৰিয়তে সংযুক্ত"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARCৰ জৰিয়তে সংযুক্ত"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"টিভি ডিফ’ল্ট"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI আউটপুট"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"অভ্যন্তৰীণ স্পীকাৰ"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 330532ec48b0..3607a97dd41f 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu planşet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Bu kompüter (daxili)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Chrome-da Tətbiqin İşləmə Müddəti vasitəsilə qoşulub"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC vasitəsilə qoşulub"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV defoltu"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI çıxışı"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Daxili dinamiklər"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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 4dfd25302708..94f4bd036b20 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ovaj računar (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano preko ARC-a"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano preko eARC-a"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Podrazumevana vrednost za TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izlaz"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Unutrašnji zvučnici"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem pri povezivanju. Isključite uređaj, pa ga ponovo uključite"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index de25460e4cbc..1903a3025935 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Гэты тэлефон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Гэты планшэт"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Гэты камп’ютар (унутраны)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Дынамік док-станцыі"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Знешняя прылада"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Падключана праз ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Падключана праз eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартны аўдыявыхад тэлевізара"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Выхад HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Унутраныя дынамікі"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 47ce37cfc900..da1561a20387 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Този телефон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Този таблет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Този компютър (вграден)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Високоговорител докинг станция"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Външно устройство"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Свързано посредством ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Свързано посредством eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартното за телевизора"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI изход"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Вградени високоговорители"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 3b77cfdae6b5..90e7e6051fb1 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফোন"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই ট্যাবলেট"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"এই কম্পিউটার (ইন্টার্নাল)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ডক স্পিকার"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"এক্সটার্নাল ডিভাইস"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC-এর মাধ্যমে কানেক্ট করা হয়েছে"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC-এর মাধ্যমে কানেক্ট করা হয়েছে"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"টিভির ডিফল্ট সেটিং"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI আউটপুট"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ইন্টার্নাল স্পিকার"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 65eb6817f631..2ac15c494af8 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ovaj računar (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano je putem ARC-a"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano je putem eARC-a"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Zadano za TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izlaz"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interni zvučnici"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index a7c3fafd48ef..dcc614ff2b16 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Aquest telèfon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Aquesta tauleta"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Aquest ordinador (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connectat mitjançant ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connectat mitjançant eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Televisor predeterminat"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Sortida HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altaveus interns"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Apaga el dispositiu i torna\'l a encendre."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 832d320cd805..3d45732e4755 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Tento počítač (interní)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Reproduktor doku"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externí zařízení"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Připojeno přes ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Připojeno přes eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Výchozí nastavení televize"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Výstup HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interní reproduktory"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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> <string name="help_label" msgid="3528360748637781274">"Nápověda a zpětná vazba"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 708d5df4c6b3..7ed51e4f6d46 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Denne tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Denne computer (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Forbundet via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Forbundet via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Standardindstillinger for fjernsyn"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-udgang"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne højttalere"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string> <string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 069201a63174..ee6234448483 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Dieses Smartphone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dieses Tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Dieser Computer (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock-Lautsprecher"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externes Gerät"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Per ARC verbunden"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Per eARC verbunden"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Standardeinstellung: Fernseher"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-Ausgang"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne Lautsprecher"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus und wieder ein."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string> <string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 8577a9a8acdf..ec3949c46666 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Αυτό το tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Αυτός ο υπολογιστής (εσωτερ.)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Ηχείο βάσης σύνδεσης"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Εξωτερική συσκευή"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Συνδέθηκε μέσω ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Συνδέθηκε μέσω eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Προεπιλογή τηλεόρασης"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Έξοδος HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Εσωτερικά ηχεία"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index f9d2f082b06c..bb1efc47307d 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV default"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Internal speakers"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 2cf98ef44c26..6d1cce60c73b 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV default"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Internal speakers"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off & back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index f9d2f082b06c..bb1efc47307d 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV default"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Internal speakers"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index f9d2f082b06c..bb1efc47307d 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV default"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Internal speakers"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index a4834d887140..f5fd36895525 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Esta computadora (interna)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Bocina de la estación de carga"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Se estableció conexión con un cable ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Se estableció conexión con un cable eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Configuración predeterminada de la TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Salida de HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Bocinas internas"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 1792fd13a5f5..b78d9fde9d8d 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Este ordenador (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altavoz de la base"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Predeterminada de la televisión"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Salida HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altavoces internos"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 5411d0bd30b5..078e038ba489 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"See telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"See tahvelarvuti"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"See arvuti (sisemine)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ühendatud ARC-i kaudu"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ühendatud eARC-i kaudu"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Teleri vaikeväljund"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-väljund"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Sisemised kõlarid"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string> <string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 090d99d3b771..cc60fd5775df 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefono hau"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tableta hau"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ordenagailu hau (barnekoa)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC bidez konektatuta"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC bidez konektatuta"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Telebistaren audio-erreproduzigailu lehenetsia"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI irteera"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Barneko bozgorailuak"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazo bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string> <string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index c350175b788d..61b0b0f7825d 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"این تلفن"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"این رایانه لوحی"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"این رایانه (داخلی)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"بلندگوی پایه اتصال"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"دستگاه خارجی"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"متصل ازطریق ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"متصل ازطریق eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"پیشفرض تلویزیون"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"خروجی HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"بلندگوهای داخلی"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 18a84b5f4ead..683c3c7d59d3 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tämä puhelin"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tämä tabletti"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Tämä tietokone (sisäinen)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Telinekaiutin"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ulkoinen laite"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Yhdistetty ARC:n kautta"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Yhdistetty eARC:n kautta"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV:n oletusasetus"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-toisto"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Sisäiset kaiuttimet"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string> <string name="help_label" msgid="3528360748637781274">"Ohjeet ja palaute"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 066244a06620..29eaee3f30cd 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Cet ordinateur (interne)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connecté par ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connecté par eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Sortie audio par défaut de la télévision"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Sortie HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Haut-parleurs internes"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez et rallumez l\'appareil"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string> <string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 84c2f15b12e2..04a212c4797a 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Cet ordinateur (interne)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connecté via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connecté via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Sortie par défaut de la TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Sortie HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Haut-parleurs internes"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string> <string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index d15b6e22e135..d5be2ce8789a 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tableta"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Este ordenador (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Opción predeterminada da televisión"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Saída HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altofalantes internos"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 5fd187f06c3c..54094cbf444a 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"આ ફોન"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"આ ટૅબ્લેટ"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"આ કમ્પ્યૂટર (આંતરિક)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ડૉક સ્પીકર"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"બહારનું ડિવાઇસ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC મારફતે કનેક્ટેડ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC મારફતે કનેક્ટેડ"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ડિવાઇસનું ડિફૉલ્ટ ઑડિયો આઉટપુટ, ટીવી"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI આઉટપુટ"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"આંતરિક સ્પીકર"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index fa0716e4ad22..1654b05a34a6 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यह फ़ोन"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यह टैबलेट"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"यह कंप्यूटर (इंटरनल)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाहरी डिवाइस"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"एचडीएमआई eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC से कनेक्ट किए गए डिवाइस"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC से कनेक्ट किए गए डिवाइस"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"टीवी की डिफ़ॉल्ट सेटिंग"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"एचडीएमआई आउटपुट"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"इंटरनल स्पीकर"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 8ad5c9d559d7..f7552b23e2e7 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ovo računalo (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano putem ARC-a"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano putem eARC-a"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Zadano za TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izlaz"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Unutarnji zvučnici"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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 18c6ea307651..2e5d6b522b10 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ez a telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ez a táblagép"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ez a számítógép (belső)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Csatlakoztatva ARC-n keresztül"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Csatlakoztatva eARC-n keresztül"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Tévé alapértelmezett eszköze"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-kimenet"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Belső hangszórók"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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 593479a4c011..cc8891997d67 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Այս հեռախոսը"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Այս պլանշետը"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Այս համակարգիչը (ներքին)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Դոկ-կայանով բարձրախոս"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Արտաքին սարք"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Միացված է ARC-ի միջոցով"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Միացված է eARC-ի միջոցով"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Հեռուստացույցի կանխադրված կարգավորումներ"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ելք"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ներքին բարձրախոսներ"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 6522e22734b9..0f01dea73bdb 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ponsel ini"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Komputer ini (internal)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Terhubung melalui ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Terhubung melalui eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV sebagai default"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Output HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Speaker internal"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat & aktifkan kembali"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string> <string name="help_label" msgid="3528360748637781274">"Bantuan & masukan"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 4a10fb3e94d3..c163cc25108e 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Þessi sími"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Þessi spjaldtölva"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Þessi tölva (innbyggður)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Tengt með ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Tengt með eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Sjálfgefið í sjónvarpi"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-úttak"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Innri hátalarar"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string> <string name="help_label" msgid="3528360748637781274">"Hjálp og ábendingar"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 21f78ddbdb72..c7eb70c92769 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Questo smartphone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Questo tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Questo computer (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"eARC HDMI"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connessione tramite ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connessione tramite eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV predefinita"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Uscita HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altoparlanti interni"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string> <string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 3b28bd2745e3..c884362a2ef1 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"הטלפון הזה"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"הטאבלט הזה"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"המחשב הזה (פנימי)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"רמקול של אביזר העגינה"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"מכשיר חיצוני"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"חיבור דרך ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"חיבור דרך eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ברירת המחדל של הטלוויזיה"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"פלט HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"רמקולים פנימיים"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index a826810508e5..bd781be2b73d 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"このデバイス"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"このタブレット"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"このパソコン(内蔵)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ホルダー スピーカー"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部デバイス"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC 経由で接続済み"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC 経由で接続済み"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"テレビのデフォルト"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 出力"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"内蔵スピーカー"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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 f321371a744e..89ef86e84217 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ეს ტელეფონი"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ამ ტაბლეტზე"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ეს კომპიუტერი (შიდა)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"სამაგრის დინამიკი"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"გარე მოწყობილობა"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"დაკავშირებულია ARC-ის მეშვეობით"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"დაკავშირებულია eARC-ის მეშვეობით"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ტელევიზორის ნაგულისხმევი"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"გამომავალი HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"შიდა დინამიკები"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 85b86d4ef392..fe4f1cdd0c71 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Осы телефон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Осы планшет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Осы компьютер (ішкі)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Динамигі бар қондыру станциясы"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Сыртқы құрылғы"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC арқылы жалғанған."</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC арқылы жалғанған."</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Теледидардың әдепкі шығысы"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI шығыс интерфейсі"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ішкі динамиктер"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 12d35cf78685..36421c9495d7 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ថេប្លេតនេះ"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"កុំព្យូទ័រនេះ (ខាងក្នុង)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ឧបាល័រជើងទម្រ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ឧបករណ៍ខាងក្រៅ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"បានភ្ជាប់តាមរយៈ ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"បានភ្ជាប់តាមរយៈ eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"លំនាំដើមទូរទស្សន៍"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"ធាតុចេញ HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ឧបករណ៍បំពងសំឡេងខាងក្នុង"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index d95846fba071..78f0a37b6201 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ಈ ಫೋನ್"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ಈ ಟ್ಯಾಬ್ಲೆಟ್"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ಈ ಕಂಪ್ಯೂಟರ್ (ಆಂತರಿಕ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ಡಾಕ್ ಸ್ಪೀಕರ್"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ಬಾಹ್ಯ ಸಾಧನ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ಮೂಲಕ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ಮೂಲಕ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ಟಿವಿ ಡೀಫಾಲ್ಟ್"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ಔಟ್ಪುಟ್"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ಆಂತರಿಕ ಸ್ಪೀಕರ್ಗಳು"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index f82a6a6ec442..9ce92221e928 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"이 휴대전화"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"이 태블릿"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"이 컴퓨터(내부)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"도크 스피커"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"외부 기기"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC를 통해 연결됨"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC를 통해 연결됨"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV 기본값"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 출력"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"내부 스피커"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index ff63c1922940..86eb12d9677c 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ушул телефон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ушул планшет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Бул компьютер (ички)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док бекеттин динамиги"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Тышкы түзмөк"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC аркылуу туташты"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC аркылуу туташты"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Cыналгыдагы демейки түзмөк"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI аудио түзмөгү"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ички динамиктер"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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 36d2ccf041db..2d800c587230 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ແທັບເລັດນີ້"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ຄອມພິວເຕີນີ້ (ພາຍໃນ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ແທ່ນວາງລຳໂພງ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ອຸປະກອນພາຍນອກ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ເຊື່ອມຕໍ່ຜ່ານ ARC ແລ້ວ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"ເຊື່ອມຕໍ່ຜ່ານ eARC ແລ້ວ"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ຄ່າເລີ່ມຕົ້ນສຳລັບໂທລະທັດ"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"ເອົ້າພຸດ HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ລຳໂພງຂອງເຄື່ອງ"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 5b20db3cd464..08bf30368cd5 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis telefonas"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetinis kompiuteris"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Šis kompiuteris (vidinis)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI „eARC“"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Prisijungta per ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Prisijungta per „eARC“"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV numatytoji išvestis"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI išvestis"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Vidiniai garsiakalbiai"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string> <string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 620318a08f18..e0ba42408894 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis tālrunis"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetdators"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Šī datora iekšējais skaļrunis"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Savienojums izveidots, izmantojot ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Savienojums izveidots, izmantojot eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Noklusējuma iestatījums televizorā"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izvade"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Iekšējie skaļruņi"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string> <string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 0331ece3c4dc..86312aa9cd24 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -359,7 +359,7 @@ <string name="enable_terminal_summary" msgid="2481074834856064500">"Овозможи апликација на терминал што овозможува локален пристап кон школка."</string> <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Програмерска околина на Linux"</string> <string name="enable_linux_terminal_summary" msgid="2029479880888108902">"(Експериментално) Вклучете го Linux-терминалот на Android"</string> - <string name="disable_linux_terminal_disclaimer" msgid="3054320531778388231">"Ако оневозможите, податоците за Linux-терминалот ќе се избришат"</string> + <string name="disable_linux_terminal_disclaimer" msgid="3054320531778388231">"Ако го оневозможите, податоците за Linux-терминалот ќе се избришат"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Проверка со HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Постави однесување на проверка на HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Отстранување грешки"</string> @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овој телефон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овој таблет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Овој компјуер (внатрешен)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док со звучник"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Надворешен уред"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Поврзано преку ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Поврзано преку eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандарден излез на телевизор"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Излез за HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Внатрешни звучници"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index aaebe4cd6abf..04596d2436bf 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ഈ ഫോൺ"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ഈ ടാബ്ലെറ്റ്"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ഈ കമ്പ്യൂട്ടർ (ഇന്റേണൽ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ഡോക്ക് സ്പീക്കർ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ബാഹ്യ ഉപകരണം"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC വഴി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC വഴി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ടിവി ഡിഫോൾട്ട്"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ഔട്ട്പുട്ട്"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ആന്തരിക സ്പീക്കറുകൾ"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index fb2b7291bb88..a4d5a8f4c2e7 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Энэ утас"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Энэ таблет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Энэ компьютер (дотоод)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Суурилуулагчийн чанга яригч"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Гадаад төхөөрөмж"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC-р холбогдсон"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC-р холбогдсон"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ТВ-ийн өгөгдмөл"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI гаралт"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Дотоод чанга яригч"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 023a2e47e8f3..0ac6b84f7779 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"हा फोन"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"हा टॅबलेट"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"हा काँप्युटर (अंतर्गत)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिव्हाइस"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC द्वारे कनेक्ट केलेली"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC द्वारे कनेक्ट केलेली"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"टीव्ही डीफॉल्ट"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI आउटपुट"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"अंतर्गत स्पीकर"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 4212eb49f2cf..888eca2fde67 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefon ini"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Komputer ini (dalaman)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Disambungkan melalui ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Disambungkan melalui eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Tetapan lalai TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Output HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Pembesar suara dalaman"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Masalah penyambungan. Matikan & hidupkan kembali peranti"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Peranti audio berwayar"</string> <string name="help_label" msgid="3528360748637781274">"Bantuan & maklum balas"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index df13e6c5f33e..0ce738150a2b 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ဤဖုန်း"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ဤတက်ဘလက်"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ဤကွန်ပျူတာ (စက်တွင်း)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"အထိုင် စပီကာ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ပြင်ပစက်"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV မူရင်း"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI အထွက်"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"စက်ရှိ စပီကာများ"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 812666350505..72bce76dac6e 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefonen"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dette nettbrettet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Denne datamaskinen (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Tilkoblet via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Tilkoblet via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV-ens standardinnstilling"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-utgang"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne høyttalere"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string> <string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index a233622362b8..cab816fc3066 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यो फोन"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यो ट्याब्लेट"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"यो कम्प्युटर (आन्तरिक)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डक स्पिकर"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिभाइस"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC मार्फत कनेक्ट गरिएका"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC मार्फत कनेक्ट गरिएका"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"टिभीको डिफल्ट अडियो आउटपुट"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI आउटपुट"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"आन्तरिक स्पिकरहरू"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 551097d9e442..22b320db21ae 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Deze telefoon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Deze tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Deze computer (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockspeaker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern apparaat"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Verbonden via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Verbonden via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Tv-standaard"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-uitvoer"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne speakers"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem bij verbinding maken. Zet het apparaat uit en weer aan."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedraad audioapparaat"</string> <string name="help_label" msgid="3528360748637781274">"Hulp en feedback"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 790acffe2b80..b8a73f7dc22b 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ଏହି ଫୋନ"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ଏହି ଟାବଲେଟ"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ଏହି କମ୍ପ୍ୟୁଟର (ଇଣ୍ଟର୍ନଲ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ଡକ ସ୍ପିକର"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ଟିଭିର ଡିଫଲ୍ଟ ଅଡିଓ ଆଉଟପୁଟ"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ଆଉଟପୁଟ"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ଇଣ୍ଟର୍ନଲ ସ୍ପିକର"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index c20e0d88ee3f..77eb146d5d52 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ਇਹ ਟੈਬਲੈੱਟ"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ਇਸ ਕੰਪਿਊਟਰ \'ਤੇ (ਅੰਦਰੂਨੀ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ਡੌਕ ਸਪੀਕਰ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤੇ ਗਏ ਡੀਵਾਈਸ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤੇ ਗਏ ਡੀਵਾਈਸ"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ਟੀਵੀ ਦਾ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਆਊਟਪੁੱਟ"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ਆਊਟਪੁੱਟ"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ਅੰਦਰੂਨੀ ਸਪੀਕਰ"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index f6ab8a7c19a4..e5cca9c375f8 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ten telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ten tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ten komputer (wewnętrzny)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Połączono przez ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Połączono przez eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Ustawienie domyślne telewizora"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Wyjście HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Głośniki wewnętrzne"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string> <string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index dedc65c1369f..a879f17bcb18 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Este computador (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Padrão da TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Saída HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Alto-falantes internos"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index f1ac88bdda40..3ae270ec0041 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telemóvel"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Este computador (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altifalante estação carregamento"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ligado através de ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ligado através de eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Predefinição da TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Saída HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altifalantes internos"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index dedc65c1369f..a879f17bcb18 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Este computador (interno)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Padrão da TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Saída HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Alto-falantes internos"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index cafb7881a32a..1fe3651bb497 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Acest telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Această tabletă"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Acest computer (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectat prin ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectat prin eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Prestabilit pentru televizor"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Ieșire HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Difuzoare interne"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Oprește și repornește dispozitivul."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string> <string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 951f3a0dd146..f8389ba3b6c5 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Этот смартфон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Этот планшет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Встроенное"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Колонка с док-станцией"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Внешнее устройство"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Подключено через ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Подключено через eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартный выход на телевизоре"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Выход HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Внутренние динамики"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 8427064b99ef..07ce339c86a6 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"මෙම දුරකථනය"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"මෙම ටැබ්ලටය"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"මෙම පරිගණකය (අභ්යන්තර)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ඩොක් ස්පීකරය"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"බාහිර උපාංගය"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC හරහා සම්බන්ධ කර ඇත"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC හරහා සම්බන්ධ කර ඇත"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"රූපවාහිනී පෙරනිමිය"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ප්රතිදානය"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"අභ්යන්තර ස්පීකර්"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index aca07e5a32c3..2a244970377a 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefón"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Tento počítač (interný)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Pripojené prostredníctvom rozhrania ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Pripojené prostredníctvom rozhrania eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Predvolené nastavenie televízora"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Výstup HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interné reproduktory"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 00d93a65d2f1..81f30634d1b1 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ta telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ta tablični računalnik"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ta računalnik (notranji)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano prek zvočnega kanala ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano prek zvočnega kanala eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Privzeti izhod televizorja"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Izhod HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Notranji zvočniki"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string> <string name="help_label" msgid="3528360748637781274">"Pomoč in povratne informacije"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index c907e22c7784..504d821a95d9 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ky tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ky kompjuter (i brendshëm)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Lidhur përmes ARC-së"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Lidhur përmes eARC-së"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Parazgjedhja e televizorit"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Dalja HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altoparlantët e brendshëm"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string> <string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 65000e468fea..b2dc5fe09e31 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овај телефон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овај таблет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Овај рачунар (интерно)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Звучник базне станице"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Спољни уређај"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Повезано преко ARC-а"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Повезано преко eARC-а"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Подразумевана вредност за ТВ"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI излаз"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Унутрашњи звучници"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 12c17290bf15..aaa90e0737f0 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Den här telefonen"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Den här surfplattan"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Den här datorn (intern)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ansluten via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ansluten via eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Standardutgång för tv:n"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-utgång"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interna högtalare"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string> <string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 1bf5677668ca..99d2949ea9d0 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Simu hii"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Kishikwambi hiki"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Kompyuta hii (spika ya ndani)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Imeunganishwa kupitia ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Imeunganishwa kupitia eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Mipangilio Chaguomsingi ya TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Kutoa sauti kupitia HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Spika za ndani"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kuna tatizo la kuunganisha kwenye Intaneti. Zima kisha uwashe kifaa"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kifaa cha sauti kinachotumia waya"</string> <string name="help_label" msgid="3528360748637781274">"Usaidizi na maoni"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 8ee8fa22c61f..fa2819c286cf 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"இந்த மொபைல்"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"இந்த டேப்லெட்"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"இந்தக் கம்ப்யூட்டர் (அகம்)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"டாக் ஸ்பீக்கர்"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"வெளிப்புறச் சாதனம்"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC மூலம் இணைக்கப்பட்டுள்ளவை"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC மூலம் இணைக்கப்பட்டுள்ளவை"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"டிவி இயல்புநிலை"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI அவுட்புட்"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"உட்புற ஸ்பீக்கர்கள்"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 46350d5bf30c..84fb65bfb561 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ఈ ఫోన్"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ఈ టాబ్లెట్"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ఈ కంప్యూటర్ (ఇంటర్నల్)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"డాక్ స్పీకర్"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ఎక్స్టర్నల్ పరికరం"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ద్వారా కనెక్ట్ చేయబడింది"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ద్వారా కనెక్ట్ చేయబడింది"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"టీవీ ఆటోమేటిక్ సెట్టింగ్"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI అవుట్పుట్"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"అంతర్గత స్పీకర్లు"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 460782af8457..22a2a5bbdec4 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -304,7 +304,7 @@ <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"เลือกเวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"เวอร์ชันของบลูทูธ MAP"</string> <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"เลือกเวอร์ชันของบลูทูธ MAP"</string> - <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ตัวแปลงสัญญาณเสียงบลูทูธ"</string> + <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ตัวแปลงรหัสเสียงบลูทูธ"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ทริกเกอร์การเลือกตัวแปลงรหัส\nเสียงบลูทูธ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"อัตราตัวอย่างเสียงบลูทูธ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"ทริกเกอร์การเลือกตัวแปลงรหัส\nเสียงบลูทูธ: อัตราตัวอย่าง"</string> @@ -313,7 +313,7 @@ <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"ทริกเกอร์การเลือกตัวแปลงรหัส\nเสียงบลูทูธ: บิตต่อตัวอย่าง"</string> <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"โหมดช่องสัญญาณเสียงบลูทูธ"</string> <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"ทริกเกอร์การเลือกตัวแปลงรหัส\nเสียงบลูทูธ: โหมดช่องสัญญาณ"</string> - <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"ตัวแปลงสัญญาณเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น"</string> + <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"ตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น"</string> <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"ทริกเกอร์การเลือกตัวแปลงรหัส LDAC\nเสียงบลูทูธ: คุณภาพการเล่น"</string> <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"สตรีมมิง: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string> <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"DNS ส่วนตัว"</string> @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"แท็บเล็ตเครื่องนี้"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"คอมพิวเตอร์เครื่องนี้ (ภายใน)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"แท่นชาร์จที่มีลำโพง"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"อุปกรณ์ภายนอก"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"เชื่อมต่อผ่าน ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"เชื่อมต่อผ่าน eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"ค่าเริ่มต้นของทีวี"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"เอาต์พุต HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ลำโพงของเครื่อง"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 986b7771d9f5..2d094e3f142c 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ang teleponong ito"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ang tablet na ito"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Sa computer na ito (internal)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Nakakonekta sa pamamagitan ng ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Nakakonekta sa pamamagitan ng eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Default ng TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Mga internal speaker"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Nagkaproblema sa pagkonekta. I-off at pagkatapos ay i-on ang device"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired na audio device"</string> <string name="help_label" msgid="3528360748637781274">"Tulong at feedback"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index df47dacc7d7b..5f15fc2d001d 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu tablet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Bu bilgisayar (dahili)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ile bağlandı"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ile bağlandı"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV varsayılanı"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI çıkışı"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Dahili hoparlörler"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string> <string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 3ba99a2f443e..0385ca0e90c2 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Цей телефон"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Цей планшет"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Цей комп’ютер (внутрішній)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Динамік док-станції"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Зовнішній пристрій"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Підключено через ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Підключено через eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартний вихід телевізора"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Вихід HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Внутрішні динаміки"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index e5e29faae8f5..85472d67ea49 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"یہ فون"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"یہ ٹیبلیٹ"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"یہ کمپیوٹر (داخلی)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ڈاک اسپیکر"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"بیرونی آلہ"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC کے ذریعے منسلک ہے"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC کے ذریعے منسلک ہے"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV ڈیفالٹ"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI آؤٹ پٹ"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"اندرونی اسپیکرز"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 2417d5556bee..d55608277de5 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Shu telefon"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Shu planshet"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Bu kompyuter (ichki)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC orqali ulangan"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC orqali ulangan"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Televizorda birlamchi"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI chiqishi"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ichki karnaylar"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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> <string name="help_label" msgid="3528360748637781274">"Yordam/fikr-mulohaza"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 0239a3f2c39d..d70019c0d7f7 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Điện thoại này"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Máy tính bảng này"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Máy tính này (nội bộ)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Đã kết nối qua ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Đã kết nối qua eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"Chế độ mặc định của TV"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Đầu ra HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Các loa trong"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sự cố kết nối. Hãy tắt thiết bị rồi bật lại"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Thiết bị âm thanh có dây"</string> <string name="help_label" msgid="3528360748637781274">"Trợ giúp và phản hồi"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 22bfda4fe6fc..1c679331c16a 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -359,7 +359,7 @@ <string name="enable_terminal_summary" msgid="2481074834856064500">"启用终端应用,以便在本地访问 Shell"</string> <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux 开发环境"</string> <string name="enable_linux_terminal_summary" msgid="2029479880888108902">"(实验性)在 Android 上运行 Linux 终端"</string> - <string name="disable_linux_terminal_disclaimer" msgid="3054320531778388231">"如果您停用它,Linux 终端数据会被清除"</string> + <string name="disable_linux_terminal_disclaimer" msgid="3054320531778388231">"停用后,Linux 终端数据会被清除"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 检查"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"设置 HDCP 检查行为"</string> <string name="debug_debugging_category" msgid="535341063709248842">"调试"</string> @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"这部手机"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"这部平板电脑"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"此计算机(内部)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"基座音箱"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"已通过 ARC 连接"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"已通过 eARC 连接"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"电视默认设置"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 输出"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"内置扬声器"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 594273a327ce..2a8c1ee5658a 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"此平板電腦"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"此電腦 (內置)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"插座喇叭"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"已透過 ARC 連接"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"已透過 eARC 連接"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"電視預設的音訊輸出"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 輸出"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"內置喇叭"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 483dbf30b31c..beb30df2d8b6 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"這支手機"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"這台平板電腦"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"這部電腦 (內部)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"座架喇叭"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"透過 ARC 連線"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"透過 eARC 連線"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"電視預設的音訊輸出裝置"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 輸出裝置"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"內建揚聲器"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="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-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 2bb3f225565e..1031bb7ac322 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -586,7 +586,7 @@ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Le foni"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Le thebhulethi"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Le khompyutha (ngaphakathi)"</string> - <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> + <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) --> <skip /> <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> @@ -607,9 +607,10 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"I-HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ixhunywe nge-ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ixhunywe nge-eARC"</string> - <string name="tv_media_transfer_default" msgid="5403053145185843843">"I-TV ezenzakalelayo"</string> - <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"umphumela we-HDMI"</string> - <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Izipikha zangaphakathi"</string> + <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) --> + <skip /> + <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) --> + <skip /> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string> <string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt index 07abb6b912b6..888f54f4bb34 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt @@ -69,8 +69,8 @@ val LocalBluetoothLeBroadcast.onBroadcastStartedOrStopped: Flow<Unit> } .buffer(capacity = Channel.CONFLATED) -/** [Flow] for [BluetoothLeBroadcast.Callback] onPlaybackStarted event */ -val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit> +/** [Flow] for [BluetoothLeBroadcast.Callback] onBroadcastMetadataChanged event */ +val LocalBluetoothLeBroadcast.onBroadcastMetadataChanged: Flow<Unit> get() = callbackFlow { val listener = @@ -87,7 +87,6 @@ val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit> } override fun onPlaybackStarted(reason: Int, broadcastId: Int) { - launch { trySend(Unit) } } override fun onPlaybackStopped(reason: Int, broadcastId: Int) { @@ -100,7 +99,9 @@ val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit> override fun onBroadcastMetadataChanged( broadcastId: Int, metadata: BluetoothLeBroadcastMetadata - ) {} + ) { + trySend(Unit) + } } registerServiceCallBack( ConcurrentUtils.DIRECT_EXECUTOR, diff --git a/packages/SettingsProvider/res/xml/bookmarks.xml b/packages/SettingsProvider/res/xml/bookmarks.xml index 645b275e2af5..cb38b53c1cbc 100644 --- a/packages/SettingsProvider/res/xml/bookmarks.xml +++ b/packages/SettingsProvider/res/xml/bookmarks.xml @@ -20,14 +20,10 @@ Typical shortcuts (not necessarily defined here): 'b': Browser - 'c': Contacts + 'p': Contacts 'e': Email - 'g': GMail - 'k': Calendar + 'c': Calendar 'm': Maps - 'p': Music - 's': SMS - 't': Talk 'u': Calculator 'y': YouTube --> @@ -40,23 +36,17 @@ shortcut="b" /> <bookmark category="android.intent.category.APP_CONTACTS" - shortcut="c" /> + shortcut="p" /> <bookmark category="android.intent.category.APP_EMAIL" shortcut="e" /> <bookmark category="android.intent.category.APP_CALENDAR" - shortcut="k" /> + shortcut="c" /> <bookmark category="android.intent.category.APP_MAPS" shortcut="m" /> <bookmark - category="android.intent.category.APP_MUSIC" - shortcut="p" /> - <bookmark - role="android.app.role.SMS" - shortcut="s" /> - <bookmark category="android.intent.category.APP_CALCULATOR" shortcut="u" /> </bookmarks> diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 8e7180c4dc8d..935ea2549d49 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -119,7 +119,8 @@ public class SystemSettings { Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, Settings.System.NOTIFICATION_COOLDOWN_ENABLED, Settings.System.NOTIFICATION_COOLDOWN_ALL, - Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED + Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, + Settings.System.PREFERRED_REGION )); if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) { settings.add(Settings.System.PEAK_REFRESH_RATE); diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index cfc7743f0a8d..9938139293fd 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -265,5 +265,6 @@ public class SystemSettingsValidators { VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR); VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.PREFERRED_REGION, ANY_STRING_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 03fea37deaf5..6128d45831fb 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -130,10 +130,12 @@ import com.google.android.collect.Sets; import libcore.util.HexEncoding; +import java.io.BufferedReader; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -380,6 +382,8 @@ public class SettingsProvider extends ContentProvider { @GuardedBy("mLock") private Handler mHandler; + private static final Set<String> sDeviceConfigAllowlistedNamespaces = new ArraySet<>(); + // We have to call in the user manager with no lock held, private volatile UserManager mUserManager; @@ -2442,6 +2446,10 @@ public class SettingsProvider extends ContentProvider { if (!isRestrictedShell && hasWritePermission) { assertCallingUserDenyList(flags); } else if (hasAllowlistPermission) { + Set<String> allowlistedDeviceConfigNamespaces = null; + if (isRestrictedShell) { + allowlistedDeviceConfigNamespaces = getAllowlistedDeviceConfigNamespaces(); + } for (String flag : flags) { boolean namespaceAllowed = false; if (isRestrictedShell) { @@ -2452,7 +2460,7 @@ public class SettingsProvider extends ContentProvider { } else { flagNamespace = flag; } - if (WritableNamespaces.ALLOWLIST.contains(flagNamespace)) { + if (allowlistedDeviceConfigNamespaces.contains(flagNamespace)) { namespaceAllowed = true; } } else { @@ -2513,6 +2521,60 @@ public class SettingsProvider extends ContentProvider { } } + /** + * Returns a Set of DeviceConfig allowlisted namespaces in which all flags can be modified + * by a caller with the {@code WRITE_ALLOWLISTED_DEVICE_CONFIG} permission. + * <p> + * This method also supports mainline modules that introduce their own allowlisted + * namespaces within the {@code etc/writable_namespaces} file under their directory. + */ + private Set<String> getAllowlistedDeviceConfigNamespaces() { + synchronized (sDeviceConfigAllowlistedNamespaces) { + if (!sDeviceConfigAllowlistedNamespaces.isEmpty()) { + return sDeviceConfigAllowlistedNamespaces; + } + if (android.provider.flags.Flags.deviceConfigWritableNamespacesApi()) { + sDeviceConfigAllowlistedNamespaces.addAll(DeviceConfig.getAdbWritableNamespaces()); + } else { + sDeviceConfigAllowlistedNamespaces.addAll(WritableNamespaces.ALLOWLIST); + } + final long identity = Binder.clearCallingIdentity(); + try { + List<String> apexDirectories; + try { + apexDirectories = mPackageManager.getAllApexDirectories(); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Caught a RemoteException obtaining APEX directories: ", e); + return sDeviceConfigAllowlistedNamespaces; + } + for (int i = 0; i < apexDirectories.size(); i++) { + String apexDirectory = apexDirectories.get(i); + File namespaceFile = Environment.buildPath(new File(apexDirectory), "etc", + "writable_namespaces"); + if (namespaceFile.exists() && namespaceFile.isFile()) { + try (BufferedReader reader = new BufferedReader( + new FileReader(namespaceFile))) { + String namespace; + while ((namespace = reader.readLine()) != null) { + namespace = namespace.trim(); + // Support comments by ignoring any lines that start with '#'. + if (!namespace.isEmpty() && !namespace.startsWith("#")) { + sDeviceConfigAllowlistedNamespaces.add(namespace); + } + } + } catch (IOException e) { + Slog.e(LOG_TAG, "Caught an exception parsing file: " + namespaceFile, + e); + } + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return sDeviceConfigAllowlistedNamespaces; + } + } + private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( int targetSdkVersion, String name) { // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java index b0409c037107..5ce97eb22a04 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java @@ -33,6 +33,7 @@ import java.util.Set; final class WritableNamespaces { public static final Set<String> ALLOWLIST = new ArraySet<String>(Arrays.asList( + "adservices", "captive_portal_login", "connectivity", "exo", diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 0ec5571a7b8f..e03afecc4240 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -990,6 +990,9 @@ <uses-permission android:name="android.permission.health.READ_SKIN_TEMPERATURE" android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/> + <!-- Permissions required for CTS test - BugreportManagerTest --> + <uses-permission android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt b/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt index 021a5143c09a..6af8004c4015 100644 --- a/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt +++ b/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt @@ -30,6 +30,7 @@ class StatementServiceApplication : Application() { // WorkManager can only schedule when the user data directories are unencrypted (after // the user has entered their lock password. DomainVerificationUtils.schedulePeriodicCheckUnlocked(WorkManager.getInstance(this)) + DomainVerificationUtils.schedulePeriodicUpdateUnlocked(WorkManager.getInstance(this)) } } } diff --git a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt index 694424822b30..157a800d0e09 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt @@ -22,6 +22,7 @@ import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import com.android.statementservice.domain.worker.RetryRequestWorker +import com.android.statementservice.domain.worker.UpdateVerifiedDomainsWorker import java.time.Duration object DomainVerificationUtils { @@ -30,6 +31,10 @@ object DomainVerificationUtils { private const val PERIODIC_SHORT_HOURS = 24L private const val PERIODIC_LONG_ID = "retry_long" private const val PERIODIC_LONG_HOURS = 72L + private const val PERIODIC_UPDATE_ID = "update" + private const val PERIODIC_UPDATE_HOURS = 720L + + private const val UPDATE_WORKER_ENABLED = false /** * In a majority of cases, the initial requests will be enough to verify domains, since they @@ -74,4 +79,38 @@ object DomainVerificationUtils { } } } + + /** + * Schedule a periodic worker to check for any updates to assetlink.json files for domains that + * have already been verified. + * + * Due to the potential for this worker to generate enough traffic across all android devices + * to overwhelm websites, this method is hardcoded to be disabled by default. It is highly + * recommended to not enable this worker and instead implement a custom worker that pulls + * updates from a caching service instead of directly from websites. + */ + fun schedulePeriodicUpdateUnlocked(workManager: WorkManager) { + if (UPDATE_WORKER_ENABLED) { + workManager.apply { + PeriodicWorkRequestBuilder<UpdateVerifiedDomainsWorker>( + Duration.ofDays( + PERIODIC_UPDATE_HOURS + ) + ) + .setConstraints( + Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresDeviceIdle(true) + .build() + ) + .build() + .let { + enqueueUniquePeriodicWork( + PERIODIC_UPDATE_ID, + ExistingPeriodicWorkPolicy.KEEP, it + ) + } + } + } + } } diff --git a/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt b/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt index 6914347544de..c7f6c184fd22 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt @@ -64,7 +64,8 @@ class DomainVerifier private constructor( private val targetAssetCache = AssetLruCache() - fun collectHosts(packageNames: Iterable<String>): Iterable<Triple<UUID, String, String>> { + fun collectHosts(packageNames: Iterable<String>, statusFilter: (Int) -> Boolean): + Iterable<Triple<UUID, String, Iterable<String>>> { return packageNames.mapNotNull { packageName -> val (domainSetId, _, hostToStateMap) = try { manager.getDomainVerificationInfo(packageName) @@ -74,14 +75,13 @@ class DomainVerifier private constructor( } ?: return@mapNotNull null val hostsToRetry = hostToStateMap - .filterValues(VerifyStatus::shouldRetry) + .filterValues(statusFilter) .takeIf { it.isNotEmpty() } ?.map { it.key } ?: return@mapNotNull null - hostsToRetry.map { Triple(domainSetId, packageName, it) } + Triple(domainSetId, packageName, hostsToRetry) } - .flatten() } suspend fun verifyHost( diff --git a/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt b/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt index 2193ec542238..c771da3ab563 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt @@ -49,7 +49,7 @@ enum class VerifyStatus(val value: Int) { return false } - val status = values().find { it.value == state } ?: return true + val status = entries.find { it.value == state } ?: return true return when (status) { SUCCESS, FAILURE_LEGACY_UNSUPPORTED_WILDCARD, @@ -62,5 +62,20 @@ enum class VerifyStatus(val value: Int) { FAILURE_REDIRECT -> true } } + + fun canUpdate(state: Int): Boolean { + if (state == DomainVerificationInfo.STATE_UNMODIFIABLE) { + return false + } + + val status = entries.find { it.value == state } + return when (status) { + SUCCESS, + FAILURE_LEGACY_UNSUPPORTED_WILDCARD, + FAILURE_REJECTED_BY_SERVER, + UNKNOWN -> true + else -> false + } + } } } diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/PeriodicUpdateWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/PeriodicUpdateWorker.kt new file mode 100644 index 000000000000..7ec6e6c28e9b --- /dev/null +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/PeriodicUpdateWorker.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.statementservice.domain.worker + +import android.content.Context +import android.content.UriRelativeFilterGroup +import android.content.pm.verify.domain.DomainVerificationManager +import androidx.work.ListenableWorker +import androidx.work.WorkerParameters +import com.android.statementservice.domain.VerifyStatus +import com.android.statementservice.utils.AndroidUtils +import com.android.statementservice.utils.StatementUtils +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.isActive + +abstract class PeriodicUpdateWorker( + appContext: Context, + params: WorkerParameters +) : BaseRequestWorker(appContext, params) { + + data class VerifyResult( + val host: String, + val status: VerifyStatus, + val groups: List<UriRelativeFilterGroup> + ) + + protected suspend fun updateDomainVerificationStatus(verifyStatusFilter: (Int) -> Boolean): + ListenableWorker.Result { + return coroutineScope { + if (!AndroidUtils.isReceiverV2Enabled(appContext)) { + return@coroutineScope Result.success() + } + + val packageNames = verificationManager.queryValidVerificationPackageNames() + + verifier.collectHosts(packageNames, verifyStatusFilter) + .map { (domainSetId, packageName, hosts) -> + hosts.map { host -> + async { + if (isActive && !isStopped) { + val (_, status, statement) = verifier.verifyHost( + host, + packageName, + params.network + ) + val groups = statement?.dynamicAppLinkComponents.orEmpty().map { + StatementUtils.createUriRelativeFilterGroup(it) + } + VerifyResult(host, status, groups) + } else { + // If the job gets cancelled, stop the remaining hosts, but continue the + // job to commit the results for hosts that were already requested. + null + } + } + }.awaitAll().filterNotNull().groupBy { it.status } + .forEach { (status, results) -> + val error = verificationManager.setDomainVerificationStatus( + domainSetId, + results.map { it.host }.toSet(), + status.value + ) + if (error == DomainVerificationManager.STATUS_OK + && status == VerifyStatus.SUCCESS + ) { + updateUriRelativeFilterGroups( + packageName, + results.associateBy({ it.host }, { it.groups }) + ) + } + } + } + + // Succeed regardless of results since this retry is best effort and not required + Result.success() + } + } +}
\ No newline at end of file diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt index f83601a7807b..e8b4df9b0943 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt @@ -17,18 +17,9 @@ package com.android.statementservice.domain.worker import android.content.Context -import android.content.UriRelativeFilterGroup -import android.content.pm.verify.domain.DomainVerificationManager import androidx.work.NetworkType import androidx.work.WorkerParameters import com.android.statementservice.domain.VerifyStatus -import com.android.statementservice.utils.AndroidUtils -import com.android.statementservice.utils.StatementUtils -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.isActive -import java.util.UUID /** * Scheduled every 24 hours with [NetworkType.CONNECTED] and every 72 hours without any constraints @@ -37,63 +28,7 @@ import java.util.UUID class RetryRequestWorker( appContext: Context, params: WorkerParameters -) : BaseRequestWorker(appContext, params) { +) : PeriodicUpdateWorker(appContext, params) { - data class VerifyResult( - val domainSetId: UUID, - val host: String, - val status: VerifyStatus, - val packageName: String, - val groups: List<UriRelativeFilterGroup> - ) - - override suspend fun doWork() = coroutineScope { - if (!AndroidUtils.isReceiverV2Enabled(appContext)) { - return@coroutineScope Result.success() - } - - val packageNames = verificationManager.queryValidVerificationPackageNames() - - verifier.collectHosts(packageNames) - .map { (domainSetId, packageName, host) -> - async { - if (isActive && !isStopped) { - val (_, status, statement) = verifier.verifyHost(host, packageName, params.network) - val groups = statement?.dynamicAppLinkComponents.orEmpty().map { - StatementUtils.createUriRelativeFilterGroup(it) - } - VerifyResult(domainSetId, host, status, packageName, groups) - } else { - // If the job gets cancelled, stop the remaining hosts, but continue the - // job to commit the results for hosts that were already requested. - null - } - } - } - .awaitAll() - .filterNotNull() // TODO(b/159952358): Fast fail packages which can't be retrieved. - .groupBy { it.packageName } - .forEach { (packageName, resultsByName) -> - val groupUpdates = mutableMapOf<String, List<UriRelativeFilterGroup>>() - resultsByName.groupBy { it.domainSetId } - .forEach { (domainSetId, resultsById) -> - resultsById.groupBy { it.status } - .forEach { (status, verifyResults) -> - val error = verificationManager.setDomainVerificationStatus( - domainSetId, - verifyResults.map(VerifyResult::host).toSet(), - status.value - ) - if (error == DomainVerificationManager.STATUS_OK - && status == VerifyStatus.SUCCESS) { - verifyResults.forEach { groupUpdates[it.host] = it.groups } - } - } - } - updateUriRelativeFilterGroups(packageName, groupUpdates) - } - - // Succeed regardless of results since this retry is best effort and not required - Result.success() - } + override suspend fun doWork() = updateDomainVerificationStatus(VerifyStatus::shouldRetry) } diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/UpdateVerifiedDomainsWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/UpdateVerifiedDomainsWorker.kt new file mode 100644 index 000000000000..c6f40c832860 --- /dev/null +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/UpdateVerifiedDomainsWorker.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.statementservice.domain.worker + +import android.content.Context +import androidx.work.WorkerParameters +import com.android.statementservice.domain.VerifyStatus + +class UpdateVerifiedDomainsWorker( + appContext: Context, + params: WorkerParameters +) : PeriodicUpdateWorker(appContext, params) { + + override suspend fun doWork() = updateDomainVerificationStatus(VerifyStatus::canUpdate) +}
\ No newline at end of file diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index d1a22e8612d3..3d250fd82473 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -81,33 +81,37 @@ filegroup { visibility: ["//visibility:private"], } -// Tests where robolectric conversion caused errors in SystemUITests at runtime +// Tests where robolectric failed at runtime. (go/central-multivalent) filegroup { - name: "SystemUI-tests-broken-robofiles-sysui-run", + name: "SystemUI-tests-broken-robofiles-run", srcs: [ + "tests/src/**/systemui/shade/NotificationShadeWindowViewControllerTest.kt", + "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt", + "tests/src/**/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt", + "tests/src/**/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt", + "tests/src/**/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt", + "tests/src/**/systemui/education/domain/ui/view/ContextualEduDialogTest.kt", + "tests/src/**/systemui/screenshot/ActionIntentCreatorTest.kt", + "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt", + "tests/src/**/systemui/accessibility/WindowMagnificationControllerTest.java", "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", - "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java", - "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt", - "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java", "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java", "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java", "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java", - "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt", "tests/src/**/systemui/settings/brightness/BrightnessDialogTest.kt", - ], -} - -// Tests where robolectric failed at runtime. (go/central-multivalent) -filegroup { - name: "SystemUI-tests-broken-robofiles-run", - srcs: [ - "tests/src/**/systemui/ExpandHelperTest.java", + "tests/src/**/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt", + "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java", + "tests/src/**/systemui/lifecycle/SysUiViewModelTest.kt", + "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt", + "tests/src/**/systemui/graphics/ImageLoaderContentProviderTest.kt", + "tests/src/**/systemui/flags/FakeFeatureFlagsTest.kt", + "tests/src/**/systemui/communal/data/backup/CommunalBackupUtilsTest.kt", "tests/src/**/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java", "tests/src/**/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java", "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java", - "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java", "tests/src/**/systemui/screenshot/appclips/AppClipsActivityTest.java", "tests/src/**/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java", "tests/src/**/systemui/screenshot/appclips/AppClipsViewModelTest.java", @@ -124,36 +128,27 @@ filegroup { "tests/src/**/systemui/classifier/FalsingDataProviderTest.java", "tests/src/**/systemui/screenshot/ImageExporterTest.java", "tests/src/**/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt", - "tests/src/**/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt", "tests/src/**/systemui/logcat/LogAccessDialogActivityTest.java", "tests/src/**/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt", "tests/src/**/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt", "tests/src/**/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java", "tests/src/**/systemui/accessibility/floatingmenu/MenuViewLayerTest.java", - "tests/src/**/systemui/accessibility/floatingmenu/MenuViewTest.java", "tests/src/**/systemui/classifier/PointerCountClassifierTest.java", "tests/src/**/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java", "tests/src/**/systemui/screenrecord/RecordingControllerTest.java", "tests/src/**/systemui/screenshot/RequestProcessorTest.kt", "tests/src/**/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt", - "tests/src/**/systemui/screenshot/SaveImageInBackgroundTaskTest.kt", "tests/src/**/systemui/screenshot/scroll/ScrollCaptureClientTest.java", "tests/src/**/systemui/accessibility/SecureSettingsContentObserverTest.java", "tests/src/**/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt", "tests/src/**/systemui/qs/external/TileServicesTest.java", "tests/src/**/systemui/ambient/touch/TouchMonitorTest.java", - "tests/src/**/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java", "tests/src/**/systemui/accessibility/WindowMagnificationSettingsTest.java", - "tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt", "tests/src/**/systemui/CameraProtectionLoaderImplTest.kt", - "tests/src/**/systemui/DependencyTest.java", - "tests/src/**/systemui/InitControllerTest.java", "tests/src/**/systemui/SliceBroadcastRelayHandlerTest.java", "tests/src/**/systemui/SystemUIApplicationTest.kt", "tests/src/**/systemui/SysUICutoutProviderTest.kt", - "tests/src/**/keyguard/ActiveUnlockConfigTest.kt", "tests/src/**/keyguard/AdminSecondaryLockScreenControllerTest.java", - "tests/src/**/keyguard/KeyguardClockAccessibilityDelegateTest.java", "tests/src/**/keyguard/KeyguardStatusViewControllerTest.java", "tests/src/**/systemui/accessibility/AccessibilityButtonModeObserverTest.java", "tests/src/**/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java", @@ -164,16 +159,12 @@ filegroup { "tests/src/**/systemui/animation/TextAnimatorTest.kt", "tests/src/**/systemui/animation/TextInterpolatorTest.kt", "tests/src/**/systemui/animation/ActivityTransitionAnimatorTest.kt", - "tests/src/**/systemui/animation/AnimatorTestRuleOrderTest.kt", "tests/src/**/systemui/animation/DialogTransitionAnimatorTest.kt", - "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", - "tests/src/**/systemui/compose/ComposeInitializerTest.kt", "tests/src/**/systemui/controls/ui/ControlsActivityTest.kt", "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt", "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt", "tests/src/**/systemui/controls/ui/DetailDialogTest.kt", - "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt", "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt", "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java", "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", @@ -182,10 +173,6 @@ filegroup { "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt", "tests/src/**/systemui/keyguard/KeyguardViewMediatorTest.java", "tests/src/**/systemui/keyguard/LifecycleTest.java", - "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt", - "tests/src/**/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt", - "tests/src/**/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt", - "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt", "tests/src/**/systemui/lifecycle/RepeatWhenAttachedTest.kt", "tests/src/**/systemui/log/LogBufferTest.kt", "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java", @@ -193,52 +180,35 @@ filegroup { "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java", "tests/src/**/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt", "tests/src/**/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt", - "tests/src/**/systemui/navigationbar/views/NavigationBarButtonTest.java", "tests/src/**/systemui/people/PeopleProviderTest.java", "tests/src/**/systemui/people/PeopleSpaceUtilsTest.java", "tests/src/**/systemui/people/widget/PeopleSpaceWidgetManagerTest.java", "tests/src/**/systemui/people/PeopleTileViewHelperTest.java", "tests/src/**/systemui/power/data/repository/PowerRepositoryImplTest.kt", - "tests/src/**/systemui/privacy/PrivacyConfigFlagsTest.kt", - "tests/src/**/systemui/privacy/PrivacyDialogV2Test.kt", - "tests/src/**/systemui/qs/external/TileRequestDialogEventLoggerTest.kt", - "tests/src/**/systemui/qs/AutoAddTrackerTest.kt", - "tests/src/**/systemui/qs/external/TileRequestDialogEventLoggerTest.kt", "tests/src/**/systemui/qs/tiles/DndTileTest.kt", "tests/src/**/systemui/qs/tiles/DreamTileTest.java", - "tests/src/**/systemui/qs/FgsManagerControllerTest.java", "tests/src/**/systemui/qs/QSPanelTest.kt", "tests/src/**/systemui/reardisplay/RearDisplayCoreStartableTest.kt", "tests/src/**/systemui/reardisplay/RearDisplayDialogControllerTest.java", "tests/src/**/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt", "tests/src/**/systemui/statusbar/KeyboardShortcutListSearchTest.java", "tests/src/**/systemui/statusbar/KeyboardShortcutsTest.java", - "tests/src/**/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt", - "tests/src/**/systemui/statusbar/notification/AssistantFeedbackControllerTest.java", "tests/src/**/systemui/statusbar/notification/collection/NotificationEntryTest.java", - "tests/src/**/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt", "tests/src/**/systemui/statusbar/notification/collection/ShadeListBuilderTest.java", - "tests/src/**/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java", "tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java", "tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt", "tests/src/**/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt", - "tests/src/**/systemui/statusbar/NotificationLockscreenUserManagerTest.java", "tests/src/**/systemui/statusbar/notification/logging/NotificationLoggerTest.java", "tests/src/**/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java", - "tests/src/**/systemui/statusbar/notification/row/NotificationContentInflaterTest.java", "tests/src/**/systemui/statusbar/notification/row/NotificationContentViewTest.kt", "tests/src/**/systemui/statusbar/notification/row/NotificationConversationInfoTest.java", - "tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerTest.java", "tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt", "tests/src/**/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt", "tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java", - "tests/src/**/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt", - "tests/src/**/systemui/statusbar/phone/AutoTileManagerTest.java", "tests/src/**/systemui/statusbar/phone/CentralSurfacesImplTest.java", "tests/src/**/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java", "tests/src/**/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt", "tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt", - "tests/src/**/systemui/statusbar/phone/PhoneStatusBarView.java", "tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewTest.kt", "tests/src/**/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt", "tests/src/**/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt", @@ -252,13 +222,9 @@ filegroup { "tests/src/**/systemui/statusbar/policy/LocationControllerImplTest.java", "tests/src/**/systemui/statusbar/policy/RemoteInputViewTest.java", "tests/src/**/systemui/statusbar/policy/SmartReplyViewTest.java", - "tests/src/**/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt", - "tests/src/**/systemui/statusbar/StatusBarStateControllerImplTest.kt", "tests/src/**/systemui/theme/ThemeOverlayApplierTest.java", "tests/src/**/systemui/touch/TouchInsetManagerTest.java", "tests/src/**/systemui/util/LifecycleFragmentTest.java", - "tests/src/**/systemui/util/kotlin/PairwiseFlowTest", - "tests/src/**/systemui/util/sensors/AsyncManagerTest.java", "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java", "tests/src/**/systemui/volume/VolumeDialogImplTest.java", "tests/src/**/systemui/wallet/controller/QuickAccessWalletControllerTest.java", @@ -271,26 +237,17 @@ filegroup { "tests/src/**/systemui/clipboardoverlay/ClipboardListenerTest.java", "tests/src/**/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt", "tests/src/**/systemui/communal/data/db/CommunalWidgetDaoTest.kt", - "tests/src/**/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt", "tests/src/**/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt", "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt", "tests/src/**/systemui/lifecycle/ActivatableTest.kt", - "tests/src/**/systemui/lifecycle/HydratorTest.kt", "tests/src/**/systemui/media/dialog/MediaSwitchingControllerTest.java", "tests/src/**/systemui/qs/QSImplTest.java", "tests/src/**/systemui/qs/panels/ui/compose/DragAndDropTest.kt", "tests/src/**/systemui/qs/panels/ui/compose/ResizingTest.kt", "tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java", - "tests/src/**/systemui/accessibility/floatingmenu/PositionTest.java", "tests/src/**/systemui/animation/TransitionAnimatorTest.kt", - "tests/src/**/systemui/screenshot/scroll/ScrollCaptureControllerTest.java", - "tests/src/**/systemui/lifecycle/SysuiViewModelTest.kt", - "tests/src/**/systemui/flags/FakeFeatureFlags.kt", "tests/src/**/systemui/animation/TransitionAnimatorTest.kt", - "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java", "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java", - "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt", - "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java", "tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java", "tests/src/**/systemui/toast/ToastUITest.java", "tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt", @@ -329,47 +286,48 @@ filegroup { "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java", "tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java", "tests/src/**/systemui/ScreenDecorationsTest.java", + "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java", "tests/src/**/keyguard/CarrierTextManagerTest.java", "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java", ], } -// Tests where robolectric failed at compile time. (go/multivalent-tests) +// Tests where compilation failed due to kotlin internal references. filegroup { - name: "SystemUI-tests-broken-robofiles-compile", + name: "SystemUI-tests-broken-robofiles-internal", srcs: [ - "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt", - "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java", - "tests/src/**/systemui/doze/DozeScreenStateTest.java", - "tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt", - "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt", - "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt", - "tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt", - "tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt", - "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt", - "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt", - "tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt", + "tests/src/**/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt", + "tests/src/**/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt", + "tests/src/**/android/systemui/statusbar/notification/icon/IconManagerTest.kt", + "tests/src/**/android/systemui/notetask/NoteTaskInitializerTest.kt", + "tests/src/**/systemui/statusbar/policy/VariableDateViewControllerTest.kt", + "tests/src/**/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt", + "tests/src/**/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt", + "tests/src/**/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt", + "tests/src/**/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt", + "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt", "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt", + "tests/src/**/systemui/statusbar/policy/WalletControllerImplTest.kt", "tests/src/**/keyguard/ClockEventControllerTest.kt", - "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt", + "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt", + "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt", "tests/src/**/systemui/broadcast/UserBroadcastDispatcherTest.kt", "tests/src/**/systemui/charging/WiredChargingRippleControllerTest.kt", "tests/src/**/systemui/clipboardoverlay/ClipboardModelTest.kt", "tests/src/**/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt", - "tests/src/**/systemui/controls/controller/ControlsBindingControllerImplTest.kt", "tests/src/**/systemui/controls/controller/ControlsControllerImplTest.kt", "tests/src/**/systemui/controls/controller/DeletionJobServiceTest.kt", - "tests/src/**/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt", + "tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt", "tests/src/**/systemui/controls/ui/ControlsUiControllerImplTest.kt", - "tests/src/**/systemui/controls/ui/ControlViewHolderTest.kt", "tests/src/**/systemui/controls/ui/SelectionItemTest.kt", "tests/src/**/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt", "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt", - "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt", + "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt", + "tests/src/**/systemui/media/controls/ui/MediaPlayerDataTest.kt", "tests/src/**/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt", "tests/src/**/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt", "tests/src/**/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt", @@ -377,7 +335,6 @@ filegroup { "tests/src/**/systemui/media/controls/ui/controller/MediaControlPanelTest.kt", "tests/src/**/systemui/media/controls/ui/controller/MediaViewControllerTest.kt", "tests/src/**/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt", - "tests/src/**/systemui/media/controls/ui/MediaPlayerDataTest.kt", "tests/src/**/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt", "tests/src/**/systemui/navigationbar/gestural/BackPanelControllerTest.kt", "tests/src/**/systemui/notetask/NoteTaskControllerTest.kt", @@ -386,61 +343,61 @@ filegroup { "tests/src/**/systemui/qs/external/CustomTileStatePersisterTest.kt", "tests/src/**/systemui/qs/external/TileRequestDialogTest.kt", "tests/src/**/systemui/qs/external/TileServiceRequestControllerTest.kt", - "tests/src/**/systemui/qs/tileimpl/TilesStatesTextTest.kt", + "tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt", "tests/src/**/systemui/qs/tiles/AlarmTileTest.kt", "tests/src/**/systemui/qs/tiles/BluetoothTileTest.kt", - "tests/src/**/systemui/screenshot/ScreenshotPolicyImplTest.kt", - "tests/src/**/systemui/settings/DisplayTrackerImplTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt", + "tests/src/**/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt", + "tests/src/**/systemui/statusbar/phone/FoldStateListenerTest.kt", + "tests/src/**/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt", + "tests/src/**/systemui/statusbar/notification/row/TextPrecomputerTest.kt", + "tests/src/**/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt", + "tests/src/**/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt", + "tests/src/**/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt", + "tests/src/**/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt", + "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt", + "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt", + "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt", + "tests/src/**/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt", + "tests/src/**/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt", + "tests/src/**/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt", + "tests/src/**/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt", + "tests/src/**/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt", + "tests/src/**/systemui/statusbar/notification/RoundableTest.kt", + "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt", + "tests/src/**/systemui/statusbar/gesture/GenericGestureDetectorTest.kt", + "tests/src/**/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt", + "tests/src/**/systemui/statusbar/connectivity/MobileStateTest.kt", + "tests/src/**/systemui/statusbar/commandline/CommandParserTest.kt", + "tests/src/**/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt", + "tests/src/**/systemui/statusbar/LightRevealScrimTest.kt", + "tests/src/**/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt", + "tests/src/**/systemui/shade/ShadeExpansionStateManagerTest.kt", + "tests/src/**/systemui/shade/ShadeHeaderControllerTest.kt", + "tests/src/**/systemui/shade/NotificationsQSContainerControllerTest.kt", "tests/src/**/systemui/settings/UserFileManagerImplTest.kt", "tests/src/**/systemui/settings/UserTrackerImplReceiveTest.kt", "tests/src/**/systemui/settings/UserTrackerImplTest.kt", "tests/src/**/systemui/shade/GlanceableHubContainerControllerTest.kt", "tests/src/**/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt", - "tests/src/**/systemui/shade/NotificationsQSContainerControllerTest.kt", - "tests/src/**/systemui/shade/ShadeExpansionStateManagerTest.kt", - "tests/src/**/systemui/shade/ShadeHeaderControllerTest.kt", - "tests/src/**/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt", - "tests/src/**/systemui/statusbar/commandline/CommandParserTest.kt", - "tests/src/**/systemui/statusbar/connectivity/MobileStateTest.kt", - "tests/src/**/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt", - "tests/src/**/systemui/statusbar/gesture/GenericGestureDetectorTest.kt", - "tests/src/**/systemui/statusbar/LightRevealScrimTest.kt", - "tests/src/**/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt", - "tests/src/**/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt", - "tests/src/**/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt", - "tests/src/**/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt", - "tests/src/**/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt", - "tests/src/**/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt", - "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt", - "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt", - "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt", - "tests/src/**/systemui/statusbar/notification/RoundableTest.kt", - "tests/src/**/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt", - "tests/src/**/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt", - "tests/src/**/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt", - "tests/src/**/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt", - "tests/src/**/systemui/statusbar/notification/row/TextPrecomputerTest.kt", - "tests/src/**/systemui/statusbar/phone/FoldStateListenerTest.kt", - "tests/src/**/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt", - "tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt", - "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt", - "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt", - "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt", - "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt", - "tests/src/**/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt", - "tests/src/**/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt", - "tests/src/**/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt", - "tests/src/**/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt", - "tests/src/**/systemui/statusbar/policy/VariableDateViewControllerTest.kt", - "tests/src/**/systemui/statusbar/policy/WalletControllerImplTest.kt", - "tests/src/**/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt", - "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt", - "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java", - "tests/src/**/systemui/shared/plugins/PluginActionManagerTest.java", - "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java", - "tests/src/**/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt", + "tests/src/**/systemui/screenshot/ScreenshotPolicyImplTest.kt", + "tests/src/**/systemui/qs/tileimpl/TilesStatesTextTest.kt", + "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt", + "tests/src/**/systemui/controls/ui/ControlViewHolderTest.kt", + "tests/src/**/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt", + "tests/src/**/systemui/controls/controller/ControlsBindingControllerImplTest.kt", + "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt", + "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt", + "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt", + "tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt", + "tests/src/**/systemui/settings/DisplayTrackerImplTest.kt", + "tests/src/**/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt", + "tests/src/**/systemui/wmshell/BubblesTest.java", ], - visibility: ["//visibility:private"], } //Create a library to expose SystemUI's resources to other modules. @@ -903,9 +860,8 @@ android_robolectric_test { ], exclude_srcs: [ ":SystemUI-tests-broken-robofiles-mockito-extended", - ":SystemUI-tests-broken-robofiles-compile", + ":SystemUI-tests-broken-robofiles-internal", ":SystemUI-tests-broken-robofiles-run", - ":SystemUI-tests-broken-robofiles-sysui-run", ], static_libs: [ "RoboTestLibraries", diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index e47704ebdf86..cc01071c91a4 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -55,14 +55,6 @@ "exclude-filter": "android.platform.tests.HomeTest#testAssistantWidget" } ] - }, - { - "name": "AndroidAutomotiveNotificationsTests", - "options" : [ - { - "include-filter": "android.platform.tests.NotificationTest" - } - ] } ], diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java index 3db61a58c7a3..6bc0f42f39aa 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java @@ -220,9 +220,6 @@ public class A11yMenuOverlayLayout { @SuppressLint("MissingPermission") private boolean isShortcutRestricted(int shortcutId) { - if (!Flags.hideRestrictedActions()) { - return false; - } final UserManager userManager = mService.getSystemService(UserManager.class); if (userManager == null) { return false; @@ -366,12 +363,11 @@ public class A11yMenuOverlayLayout { if (mLayout.getVisibility() == View.VISIBLE) { mLayout.setVisibility(View.GONE); } else { - if (Flags.hideRestrictedActions()) { - // Reconfigure the shortcut list in case the set of restricted actions has changed. - mA11yMenuViewPager.configureViewPagerAndFooter( - mLayout, createShortcutList(), getPageIndex()); - updateViewLayout(); - } + // Reconfigure the shortcut list in case the set of restricted actions has changed. + mA11yMenuViewPager.configureViewPagerAndFooter( + mLayout, createShortcutList(), getPageIndex()); + updateViewLayout(); + mLayout.setVisibility(View.VISIBLE); } } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java index 4ab771be55f4..71726199aeb6 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java @@ -46,9 +46,6 @@ import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.os.PowerManager; import android.os.UserManager; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.uiautomator_helpers.WaitUtils; import android.provider.Settings; import android.util.Log; @@ -63,7 +60,6 @@ import androidx.test.uiautomator.Configurator; import androidx.test.uiautomator.UiDevice; import com.android.compatibility.common.util.TestUtils; -import com.android.systemui.accessibility.accessibilitymenu.Flags; import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId; import org.junit.After; @@ -71,7 +67,6 @@ import org.junit.AfterClass; import org.junit.Assume; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,9 +77,6 @@ import java.util.concurrent.atomic.AtomicInteger; @RunWith(AndroidJUnit4.class) public class AccessibilityMenuServiceTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private static final String TAG = "A11yMenuServiceTest"; private static final int CLICK_ID = AccessibilityNodeInfo.ACTION_CLICK; @@ -499,7 +491,6 @@ public class AccessibilityMenuServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_HIDE_RESTRICTED_ACTIONS) public void testRestrictedActions_BrightnessNotAvailable() throws Throwable { try { setUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, true); @@ -519,7 +510,6 @@ public class AccessibilityMenuServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_HIDE_RESTRICTED_ACTIONS) public void testRestrictedActions_VolumeNotAvailable() throws Throwable { try { setUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, true); diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig index 10d7352da7dc..e3f5378175d2 100644 --- a/packages/SystemUI/aconfig/biometrics_framework.aconfig +++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig @@ -12,3 +12,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "cont_auth_plugin" + namespace: "biometrics_framework" + description: "Plugin and related API hooks for contextual auth plugins" + bug: "373600589" +} diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index e59fe47194da..0d654d9c8f67 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1201,6 +1201,16 @@ flag { } flag { + name: "communal_hub_use_thread_pool_for_widgets" + namespace: "systemui" + description: "Use a dedicated thread pool executor for loading widgets on glanceable hub" + bug: "369412569" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "communal_standalone_support" namespace: "systemui" description: "Support communal features without a dock" @@ -1509,6 +1519,16 @@ flag { } flag { + name: "sim_pin_use_slot_id" + namespace: "systemui" + description: "Reorient SIM data processing around slotId instead of subId" + bug: "376173142" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "use_transitions_for_keyguard_occluded" namespace: "systemui" description: "Use Keyguard Transitions to set Notification Shade occlusion state" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt index f1cbba7272b0..a1f0c146c507 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -702,6 +702,8 @@ constructor( object : Controller by controller { override val isLaunching: Boolean = false } + // Cross-task close transitions should not use this animation, so we only register it for + // when the opening window is Launcher. val returnFilter = TransitionFilter().apply { mRequirements = @@ -710,7 +712,11 @@ constructor( mActivityType = WindowConfiguration.ACTIVITY_TYPE_STANDARD mModes = intArrayOf(TRANSIT_CLOSE, TRANSIT_TO_BACK) mTopActivity = component - } + }, + TransitionFilter.Requirement().apply { + mActivityType = WindowConfiguration.ACTIVITY_TYPE_HOME + mModes = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT) + }, ) } val returnRemoteTransition = @@ -861,6 +867,9 @@ constructor( ) { // Raise closing task to "above" layer so it isn't covered. t.setLayer(target.leash, aboveLayers - i) + } else if (TransitionUtil.isOpeningType(change.mode)) { + // Put into the "below" layer space. + t.setLayer(target.leash, belowLayers - i) } } else if (TransitionInfo.isIndependent(change, info)) { // Root tasks @@ -1141,7 +1150,7 @@ constructor( // If a [controller.windowAnimatorState] exists, treat this like a takeover. takeOverAnimationInternal( window, - startWindowStates = null, + startWindowState = null, startTransaction = null, callback, ) @@ -1156,22 +1165,23 @@ constructor( callback: IRemoteAnimationFinishedCallback?, ) { val window = setUpAnimation(apps, callback) ?: return - takeOverAnimationInternal(window, startWindowStates, startTransaction, callback) + val startWindowState = startWindowStates[apps!!.indexOf(window)] + takeOverAnimationInternal(window, startWindowState, startTransaction, callback) } private fun takeOverAnimationInternal( window: RemoteAnimationTarget, - startWindowStates: Array<WindowAnimationState>?, + startWindowState: WindowAnimationState?, startTransaction: SurfaceControl.Transaction?, callback: IRemoteAnimationFinishedCallback?, ) { val useSpring = - !controller.isLaunching && startWindowStates != null && startTransaction != null + !controller.isLaunching && startWindowState != null && startTransaction != null startAnimation( window, navigationBar = null, useSpring, - startWindowStates, + startWindowState, startTransaction, callback, ) @@ -1281,7 +1291,7 @@ constructor( window: RemoteAnimationTarget, navigationBar: RemoteAnimationTarget? = null, useSpring: Boolean = false, - startingWindowStates: Array<WindowAnimationState>? = null, + startingWindowState: WindowAnimationState? = null, startTransaction: SurfaceControl.Transaction? = null, iCallback: IRemoteAnimationFinishedCallback? = null, ) { @@ -1327,6 +1337,7 @@ constructor( val isExpandingFullyAbove = transitionAnimator.isExpandingFullyAbove(controller.transitionContainer, endState) + val windowState = startingWindowState ?: controller.windowAnimatorState // We animate the opening window and delegate the view expansion to [this.controller]. val delegate = this.controller @@ -1349,18 +1360,6 @@ constructor( } } - // The states are sorted matching the changes inside the transition info. - // Using this info, the RemoteAnimationTargets are created, with their - // prefixOrderIndex fields in reverse order to that of changes. To extract - // the right state, we need to invert again. - val windowState = - if (startingWindowStates != null) { - startingWindowStates[ - startingWindowStates.size - window.prefixOrderIndex] - } else { - controller.windowAnimatorState - } - // TODO(b/323863002): use the timestamp and velocity to update the initial // position. val bounds = windowState?.bounds @@ -1449,12 +1448,6 @@ constructor( delegate.onTransitionAnimationProgress(state, progress, linearProgress) } } - val windowState = - if (startingWindowStates != null) { - startingWindowStates[startingWindowStates.size - window.prefixOrderIndex] - } else { - controller.windowAnimatorState - } val velocityPxPerS = if (longLivedReturnAnimationsEnabled() && windowState?.velocityPxPerMs != null) { val xVelocityPxPerS = windowState.velocityPxPerMs.x * 1000 @@ -1473,6 +1466,7 @@ constructor( fadeWindowBackgroundLayer = !controller.isBelowAnimatingWindow, drawHole = !controller.isBelowAnimatingWindow, startVelocity = velocityPxPerS, + startFrameTime = windowState?.timestamp ?: -1, ) } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt index e2bc4095e1b5..4e889e946a5f 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt @@ -27,6 +27,8 @@ import android.graphics.drawable.GradientDrawable import android.util.FloatProperty import android.util.Log import android.util.MathUtils +import android.util.TimeUtils +import android.view.Choreographer import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay @@ -366,6 +368,7 @@ class TransitionAnimator( @get:VisibleForTesting val springY: SpringAnimation, @get:VisibleForTesting val springScale: SpringAnimation, private val springState: SpringState, + private val startFrameTime: Long, private val onAnimationStart: Runnable, ) : Animation { @get:VisibleForTesting @@ -374,6 +377,42 @@ class TransitionAnimator( override fun start() { onAnimationStart.run() + + // If no start frame time is provided, we start the springs normally. + if (startFrameTime < 0) { + startSprings() + return + } + + // This function is not guaranteed to be called inside a frame. We try to access the + // frame time immediately, but if we're not inside a frame this will throw an exception. + // We must then post a callback to be run at the beginning of the next frame. + try { + initAndStartSprings(Choreographer.getInstance().frameTime) + } catch (_: IllegalStateException) { + Choreographer.getInstance().postFrameCallback { frameTimeNanos -> + initAndStartSprings(frameTimeNanos / TimeUtils.NANOS_PER_MS) + } + } + } + + private fun initAndStartSprings(frameTime: Long) { + // Initialize the spring as if it had started at the time that its start state + // was created. + springX.doAnimationFrame(startFrameTime) + springY.doAnimationFrame(startFrameTime) + springScale.doAnimationFrame(startFrameTime) + // Move the spring time forward to the current frame, so it updates its internal state + // following the initial momentum over the elapsed time. + springX.doAnimationFrame(frameTime) + springY.doAnimationFrame(frameTime) + springScale.doAnimationFrame(frameTime) + // Actually start the spring. We do this after the previous calls because the framework + // doesn't like it when you call doAnimationFrame() after start() with an earlier time. + startSprings() + } + + private fun startSprings() { springX.start() springY.start() springScale.start() @@ -471,7 +510,9 @@ class TransitionAnimator( * is true. * * If [startVelocity] (expressed in pixels per second) is not null, a multi-spring animation - * using it for the initial momentum will be used instead of the default interpolators. + * using it for the initial momentum will be used instead of the default interpolators. In this + * case, [startFrameTime] (if non-negative) represents the frame time at which the springs + * should be started. */ fun startAnimation( controller: Controller, @@ -480,6 +521,7 @@ class TransitionAnimator( fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, startVelocity: PointF? = null, + startFrameTime: Long = -1, ): Animation { if (!controller.isLaunching) assertReturnAnimations() if (startVelocity != null) assertLongLivedReturnAnimations() @@ -502,6 +544,7 @@ class TransitionAnimator( fadeWindowBackgroundLayer, drawHole, startVelocity, + startFrameTime, ) .apply { start() } } @@ -515,6 +558,7 @@ class TransitionAnimator( fadeWindowBackgroundLayer: Boolean = true, drawHole: Boolean = false, startVelocity: PointF? = null, + startFrameTime: Long = -1, ): Animation { val transitionContainer = controller.transitionContainer val transitionContainerOverlay = transitionContainer.overlay @@ -537,6 +581,7 @@ class TransitionAnimator( startState, endState, startVelocity, + startFrameTime, windowBackgroundLayer, transitionContainer, transitionContainerOverlay, @@ -722,6 +767,7 @@ class TransitionAnimator( startState: State, endState: State, startVelocity: PointF, + startFrameTime: Long, windowBackgroundLayer: GradientDrawable, transitionContainer: View, transitionContainerOverlay: ViewGroupOverlay, @@ -912,7 +958,7 @@ class TransitionAnimator( } } - return MultiSpringAnimation(springX, springY, springScale, springState) { + return MultiSpringAnimation(springX, springY, springScale, springState, startFrameTime) { onAnimationStart( controller, isExpandingFullyAbove, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index ae92d259d62b..85f549d43a11 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope @@ -71,9 +72,7 @@ constructor( } @Composable - override fun SceneScope.Content( - modifier: Modifier, - ) = + override fun SceneScope.Content(modifier: Modifier) = BouncerScene( viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() }, dialogFactory = dialogFactory, @@ -89,6 +88,8 @@ private fun SceneScope.BouncerScene( ) { val backgroundColor = MaterialTheme.colorScheme.surface + DisposableEffect(Unit) { onDispose { viewModel.onUiDestroyed() } } + Box(modifier) { Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) { drawRect(color = backgroundColor) @@ -101,7 +102,7 @@ private fun SceneScope.BouncerScene( dialogFactory, Modifier.element(Bouncer.Elements.Content) .sysuiResTag(Bouncer.TestTags.Root) - .fillMaxSize() + .fillMaxSize(), ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 5b1203fa17c9..787edfb9168c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -141,6 +141,7 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.customActions +import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.onClick import androidx.compose.ui.semantics.paneTitle import androidx.compose.ui.semantics.semantics @@ -902,11 +903,17 @@ private fun EmptyStateCta(contentPadding: PaddingValues, viewModel: BaseCommunal Arrangement.spacedBy(Dimensions.Spacing, Alignment.CenterVertically), horizontalAlignment = Alignment.CenterHorizontally, ) { + val titleForEmptyStateCTA = stringResource(R.string.title_for_empty_state_cta) Text( - text = stringResource(R.string.title_for_empty_state_cta), + text = titleForEmptyStateCTA, style = MaterialTheme.typography.displaySmall, textAlign = TextAlign.Center, color = colors.secondary, + modifier = + Modifier.focusable().semantics(mergeDescendants = true) { + contentDescription = titleForEmptyStateCTA + heading() + }, ) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Button( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt index 3707a87249b9..f2edec657cd4 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt @@ -27,6 +27,8 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.pointer.motionEventSpy +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import com.android.systemui.communal.ui.viewmodel.CommunalViewModel @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) @@ -42,6 +44,9 @@ fun CommunalTouchableSurface( Box( modifier = modifier + // The touchable surface is hidden for accessibility because these actions are + // already provided through custom accessibility actions. + .semantics { hideFromAccessibility() } .combinedClickable( onLongClick = viewModel::onLongClick, onClick = viewModel::onClick, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index ef5e90bd7aad..7a500805809d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -94,7 +94,7 @@ internal constructor( private val scope: CoroutineScope, private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean, ) { - var draggingItemKey by mutableStateOf<Any?>(null) + var draggingItemKey by mutableStateOf<String?>(null) private set var isDraggingToRemove by mutableStateOf(false) @@ -138,7 +138,7 @@ internal constructor( // before content padding from the initial pointer position .firstItemAtOffset(normalizedOffset - contentOffset) ?.apply { - draggingItemKey = key + draggingItemKey = key as String draggingItemInitialOffset = this.offset.toOffset() return true } @@ -284,7 +284,9 @@ fun Modifier.dragContainer( contentOffset, ) ) { - viewModel.onReorderWidgetStart() + // draggingItemKey is guaranteed to be non-null here because it is set in + // onDragStart() + viewModel.onReorderWidgetStart(dragDropState.draggingItemKey!!) } }, onDragEnd = { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt new file mode 100644 index 000000000000..e3310780afd7 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.ui.compose + +import android.content.res.Configuration +import androidx.compose.foundation.OverscrollEffect +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollableDefaults +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyGridScope +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid +import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.foundation.rememberOverscrollEffect +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.coerceAtMost +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.times + +/** + * Renders a responsive [LazyHorizontalGrid] with dynamic columns and rows. Each cell will maintain + * the specified aspect ratio, but is otherwise resizeable in order to best fill the available + * space. + */ +@Composable +fun ResponsiveLazyHorizontalGrid( + cellAspectRatio: Float, + modifier: Modifier = Modifier, + state: LazyGridState = rememberLazyGridState(), + minContentPadding: PaddingValues = PaddingValues(0.dp), + minHorizontalArrangement: Dp = 0.dp, + minVerticalArrangement: Dp = 0.dp, + flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), + userScrollEnabled: Boolean = true, + overscrollEffect: OverscrollEffect? = rememberOverscrollEffect(), + content: LazyGridScope.(sizeInfo: SizeInfo) -> Unit, +) { + check(cellAspectRatio > 0f) { "Aspect ratio must be greater than 0, but was $cellAspectRatio" } + check(minHorizontalArrangement.value >= 0f && minVerticalArrangement.value >= 0f) { + "Horizontal and vertical arrangements must be non-negative, but were " + + "$minHorizontalArrangement and $minVerticalArrangement, respectively." + } + BoxWithConstraints(modifier) { + val gridSize = rememberGridSize(maxWidth = maxWidth, maxHeight = maxHeight) + val layoutDirection = LocalLayoutDirection.current + + val minStartPadding = minContentPadding.calculateStartPadding(layoutDirection) + val minEndPadding = minContentPadding.calculateEndPadding(layoutDirection) + val minTopPadding = minContentPadding.calculateTopPadding() + val minBottomPadding = minContentPadding.calculateBottomPadding() + val minHorizontalPadding = minStartPadding + minEndPadding + val minVerticalPadding = minTopPadding + minBottomPadding + + // Determine the maximum allowed cell width and height based on the available width and + // height, and the desired number of columns and rows. + val maxCellWidth = + calculateCellSize( + availableSpace = maxWidth, + padding = minHorizontalPadding, + numCells = gridSize.width, + cellSpacing = minHorizontalArrangement, + ) + val maxCellHeight = + calculateCellSize( + availableSpace = maxHeight, + padding = minVerticalPadding, + numCells = gridSize.height, + cellSpacing = minVerticalArrangement, + ) + + // Constrain the max size to the desired aspect ratio. + val finalSize = + calculateClosestSize( + maxWidth = maxCellWidth, + maxHeight = maxCellHeight, + aspectRatio = cellAspectRatio, + ) + + // Determine how much space in each dimension we've used up, and how much we have left as + // extra space. Distribute the extra space evenly along the content padding. + val usedWidth = + calculateUsedSpace( + cellSize = finalSize.width, + numCells = gridSize.width, + padding = minHorizontalPadding, + cellSpacing = minHorizontalArrangement, + ) + .coerceAtMost(maxWidth) + val usedHeight = + calculateUsedSpace( + cellSize = finalSize.height, + numCells = gridSize.height, + padding = minVerticalPadding, + cellSpacing = minVerticalArrangement, + ) + .coerceAtMost(maxHeight) + val extraWidth = maxWidth - usedWidth + val extraHeight = maxHeight - usedHeight + + val finalContentPadding = + PaddingValues( + start = minStartPadding + extraWidth / 2, + end = minEndPadding + extraWidth / 2, + top = minTopPadding + extraHeight / 2, + bottom = minBottomPadding + extraHeight / 2, + ) + + LazyHorizontalGrid( + rows = GridCells.Fixed(gridSize.height), + modifier = Modifier.fillMaxSize(), + state = state, + contentPadding = finalContentPadding, + horizontalArrangement = Arrangement.spacedBy(minHorizontalArrangement), + verticalArrangement = Arrangement.spacedBy(minVerticalArrangement), + flingBehavior = flingBehavior, + userScrollEnabled = userScrollEnabled, + overscrollEffect = overscrollEffect, + ) { + content( + SizeInfo( + cellSize = finalSize, + contentPadding = finalContentPadding, + horizontalArrangement = minHorizontalArrangement, + verticalArrangement = minVerticalArrangement, + maxHeight = maxHeight, + ) + ) + } + } +} + +private fun calculateCellSize(availableSpace: Dp, padding: Dp, numCells: Int, cellSpacing: Dp): Dp = + (availableSpace - padding - cellSpacing * (numCells - 1)) / numCells + +private fun calculateUsedSpace(cellSize: Dp, numCells: Int, padding: Dp, cellSpacing: Dp): Dp = + cellSize * numCells + padding + (numCells - 1) * cellSpacing + +private fun calculateClosestSize(maxWidth: Dp, maxHeight: Dp, aspectRatio: Float): DpSize { + return if (maxWidth / maxHeight > aspectRatio) { + // Target is too wide, shrink width + DpSize(maxHeight * aspectRatio, maxHeight) + } else { + // Target is too tall, shrink height + DpSize(maxWidth, maxWidth / aspectRatio) + } +} + +/** + * Provides size info of the responsive grid, since the size is dynamic. + * + * @property cellSize The size of each cell in the grid. + * @property contentPadding The final content padding of the grid. + * @property horizontalArrangement The space between columns in the grid. + * @property verticalArrangement The space between rows in the grid. + * @property availableHeight The maximum height an item in the grid may occupy. + */ +data class SizeInfo( + val cellSize: DpSize, + val contentPadding: PaddingValues, + val horizontalArrangement: Dp, + val verticalArrangement: Dp, + private val maxHeight: Dp, +) { + val availableHeight: Dp + get() = + maxHeight - + contentPadding.calculateBottomPadding() - + contentPadding.calculateTopPadding() +} + +@Composable +private fun rememberGridSize(maxWidth: Dp, maxHeight: Dp): IntSize { + val configuration = LocalConfiguration.current + val orientation = configuration.orientation + + return remember(orientation, maxWidth, maxHeight) { + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + IntSize( + width = calculateNumCellsWidth(maxWidth), + height = calculateNumCellsHeight(maxHeight), + ) + } else { + // In landscape we invert the rows/columns to ensure we match the same area as portrait. + // This keeps the number of elements in the grid consistent when changing orientation. + IntSize( + width = calculateNumCellsHeight(maxWidth), + height = calculateNumCellsWidth(maxHeight), + ) + } + } +} + +private fun calculateNumCellsWidth(width: Dp) = + // See https://developer.android.com/develop/ui/views/layout/use-window-size-classes + when { + width >= 840.dp -> 3 + width >= 600.dp -> 2 + else -> 1 + } + +private fun calculateNumCellsHeight(height: Dp) = + // See https://developer.android.com/develop/ui/views/layout/use-window-size-classes + when { + height >= 900.dp -> 3 + height >= 480.dp -> 2 + else -> 1 + } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt index f84865ffc6af..3fce89054b20 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt @@ -44,8 +44,11 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace +import com.android.systemui.plugins.qs.TileDetailsViewModel import com.android.systemui.qs.composefragment.ui.GridAnchor +import com.android.systemui.qs.flags.QsDetailedView import com.android.systemui.qs.panels.ui.compose.EditMode +import com.android.systemui.qs.panels.ui.compose.TileDetails import com.android.systemui.qs.panels.ui.compose.TileGrid import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel @@ -116,24 +119,50 @@ constructor( } } +// A sealed interface to represent the possible states of the `ShadeBody` +sealed interface ShadeBodyState { + data object Editing : ShadeBodyState + data object TileDetails : ShadeBodyState + data object Default : ShadeBodyState +} + +// Function to map the current state of the `ShadeBody` +fun checkQsState(isEditing: Boolean, tileDetails: TileDetailsViewModel?): ShadeBodyState { + if (isEditing) { + return ShadeBodyState.Editing + } else if (tileDetails != null && QsDetailedView.isEnabled) { + return ShadeBodyState.TileDetails + } + return ShadeBodyState.Default +} + @Composable fun SceneScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) { val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle() + val tileDetails = viewModel.detailsViewModel.activeTileDetails AnimatedContent( - targetState = isEditing, + targetState = checkQsState(isEditing, tileDetails), transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) }, - ) { editing -> - if (editing) { - EditMode( - viewModel = viewModel.editModeViewModel, - modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding), - ) - } else { - QuickSettingsLayout( - viewModel = viewModel, - modifier = Modifier.sysuiResTag("quick_settings_panel"), - ) + ) { state -> + when (state) { + ShadeBodyState.Editing -> { + EditMode( + viewModel = viewModel.editModeViewModel, + modifier = Modifier + .fillMaxWidth() + .padding(QuickSettingsShade.Dimensions.Padding), + ) + } + ShadeBodyState.TileDetails -> { + TileDetails(viewModel.detailsViewModel) + } + else -> { + QuickSettingsLayout( + viewModel = viewModel, + modifier = Modifier.sysuiResTag("quick_settings_panel"), + ) + } } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index aa95abb3528f..aa95abb3528f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java index 944066fa4954..d47ec8cfe4e5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java @@ -25,15 +25,12 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import android.util.Size; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.FakeSharedPreferences; @@ -59,18 +56,6 @@ public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { mWindowMagnificationFrameSizePrefs = new WindowMagnificationFrameSizePrefs(mContext); } - @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) - @Test - public void saveSizeForCurrentDensity_getExpectedSize() { - Size testSize = new Size(500, 500); - mWindowMagnificationFrameSizePrefs - .saveIndexAndSizeForCurrentDensity(MagnificationSize.CUSTOM, testSize); - - assertThat(mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity()) - .isEqualTo(testSize); - } - - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void saveSizeForCurrentDensity_validPreference_getExpectedSize() { int testIndex = MagnificationSize.MEDIUM; @@ -81,7 +66,6 @@ public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { .isEqualTo(testSize); } - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void saveSizeForCurrentDensity_validPreference_getExpectedIndex() { int testIndex = MagnificationSize.MEDIUM; @@ -92,7 +76,6 @@ public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { .isEqualTo(testIndex); } - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void saveSizeForCurrentDensity_invalidPreference_getDefaultIndex() { mSharedPreferences diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt index 0bd00fb0a0e9..f1401f18f1d9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt @@ -50,8 +50,8 @@ import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock import org.mockito.Mockito.spy import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations private const val ON: Int = 1 private const val OFF: Int = 0 @@ -103,7 +103,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { systemClock, userTracker, mainHandler, - backgroundDelayableExecutor + backgroundDelayableExecutor, ) ) @@ -116,7 +116,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { sysuiState, fakeBroadcastDispatcher, mDialogTransitionAnimator, - fontScalingDialogDelegate + fontScalingDialogDelegate, ) whenever(dialogFactory.create(any(), any())).thenReturn(dialog) @@ -132,7 +132,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { systemSettings.getFloatForUser( Settings.System.FONT_SCALE, /* def= */ 1.0f, - userTracker.userId + userTracker.userId, ) assertThat(currentScale).isEqualTo(fontSizeValueArray[progress].toFloat()) @@ -163,7 +163,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { systemSettings.getFloatForUser( Settings.System.FONT_SCALE, /* def= */ 1.0f, - userTracker.userId + userTracker.userId, ) assertThat(seekBar.getProgress()).isEqualTo(1) assertThat(currentScale).isEqualTo(fontSizeValueArray[1].toFloat()) @@ -194,7 +194,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { systemSettings.getFloatForUser( Settings.System.FONT_SCALE, /* def= */ 1.0f, - userTracker.userId + userTracker.userId, ) assertThat(seekBar.getProgress()).isEqualTo(fontSizeValueArray.size - 2) assertThat(currentScale) @@ -211,7 +211,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { secureSettings.putIntForUser( Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, OFF, - userTracker.userId + userTracker.userId, ) // Default seekbar progress for font size is 1, click start icon to decrease the progress @@ -224,7 +224,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { secureSettings.getIntForUser( Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, /* def = */ OFF, - userTracker.userId + userTracker.userId, ) assertThat(currentSettings).isEqualTo(ON) @@ -256,7 +256,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { systemSettings.getFloatForUser( Settings.System.FONT_SCALE, /* def= */ 1.0f, - userTracker.userId + userTracker.userId, ) assertThat(systemScale).isEqualTo(1.0f) @@ -269,7 +269,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { // SeekBar interaction is finalized. changeListener.onUserInteractionFinalized( seekBar, - OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER + OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER, ) backgroundDelayableExecutor.runAllReady() backgroundDelayableExecutor.advanceClockToNext() @@ -280,7 +280,7 @@ class FontScalingDialogDelegateTest : SysuiTestCase() { systemSettings.getFloatForUser( Settings.System.FONT_SCALE, /* def= */ 1.0f, - userTracker.userId + userTracker.userId, ) assertThat(systemScale).isEqualTo(fontSizeValueArray[0].toFloat()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryTest.kt new file mode 100644 index 000000000000..d6ba98d65d15 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryTest.kt @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.activity.data.repository + +import android.app.ActivityManager +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING +import android.app.activityManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.log.core.Logger +import com.android.systemui.log.logcatLogBuffer +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ActivityManagerRepositoryTest : SysuiTestCase() { + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + private val logger = Logger(logcatLogBuffer("ActivityManagerRepositoryTest"), "tag") + + private val Kosmos.underTest by Kosmos.Fixture { realActivityManagerRepository } + + @Test + fun createIsAppVisibleFlow_fetchesInitialValue_true() = + kosmos.runTest { + whenever(activityManager.getUidImportance(THIS_UID)).thenReturn(IMPORTANCE_FOREGROUND) + + val latest by + collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG)) + + assertThat(latest).isTrue() + } + + @Test + fun createIsAppVisibleFlow_fetchesInitialValue_false() = + kosmos.runTest { + whenever(activityManager.getUidImportance(THIS_UID)).thenReturn(IMPORTANCE_GONE) + + val latest by + collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG)) + + assertThat(latest).isFalse() + } + + @Test + fun createIsAppVisibleFlow_getsImportanceUpdates() = + kosmos.runTest { + val latest by + collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG)) + + val listenerCaptor = argumentCaptor<ActivityManager.OnUidImportanceListener>() + verify(activityManager).addOnUidImportanceListener(listenerCaptor.capture(), any()) + val listener = listenerCaptor.firstValue + + listener.onUidImportance(THIS_UID, IMPORTANCE_GONE) + assertThat(latest).isFalse() + + listener.onUidImportance(THIS_UID, IMPORTANCE_FOREGROUND) + assertThat(latest).isTrue() + + listener.onUidImportance(THIS_UID, IMPORTANCE_TOP_SLEEPING) + assertThat(latest).isFalse() + } + + @Test + fun createIsAppVisibleFlow_ignoresUpdatesForOtherUids() = + kosmos.runTest { + val latest by + collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG)) + + val listenerCaptor = argumentCaptor<ActivityManager.OnUidImportanceListener>() + verify(activityManager).addOnUidImportanceListener(listenerCaptor.capture(), any()) + val listener = listenerCaptor.firstValue + + listener.onUidImportance(THIS_UID, IMPORTANCE_GONE) + assertThat(latest).isFalse() + + // WHEN another UID becomes foreground + listener.onUidImportance(THIS_UID + 2, IMPORTANCE_FOREGROUND) + + // THEN this UID still stays not visible + assertThat(latest).isFalse() + } + + @Test + fun createIsAppVisibleFlow_securityExceptionOnUidRegistration_ok() = + kosmos.runTest { + whenever(activityManager.getUidImportance(THIS_UID)).thenReturn(IMPORTANCE_GONE) + whenever(activityManager.addOnUidImportanceListener(any(), any())) + .thenThrow(SecurityException()) + + val latest by + collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG)) + + // Verify no crash, and we get a value emitted + assertThat(latest).isFalse() + } + + /** Regression test for b/216248574. */ + @Test + fun createIsAppVisibleFlow_getUidImportanceThrowsException_ok() = + kosmos.runTest { + whenever(activityManager.getUidImportance(any())).thenThrow(SecurityException()) + + val latest by + collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG)) + + // Verify no crash, and we get a value emitted + assertThat(latest).isFalse() + } + + companion object { + private const val THIS_UID = 558 + private const val LOG_TAG = "LogTag" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt index cdda9ccc9b9e..41cc6ee182cf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt @@ -48,8 +48,8 @@ import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 91f9cce5b69b..b8d4bb4b8e77 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -667,6 +667,7 @@ open class AuthContainerViewTest : SysuiTestCase() { faceProps, wakefulnessLifecycle, userManager, + null /* authContextPlugins */, lockPatternUtils, interactionJankMonitor, { promptSelectorInteractor }, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java index 2dcbdc80f695..2817f5573865 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -117,6 +117,7 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Random; @RunWith(AndroidJUnit4.class) @@ -1187,7 +1188,8 @@ public class AuthControllerTest extends SysuiTestCase { TestableAuthController(Context context) { super(context, null /* applicationCoroutineScope */, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, - mFingerprintManager, mFaceManager, () -> mUdfpsController, mDisplayManager, + mFingerprintManager, mFaceManager, Optional.empty(), + () -> mUdfpsController, mDisplayManager, mWakefulnessLifecycle, mUserManager, mLockPatternUtils, () -> mUdfpsLogger, () -> mLogContextInteractor, () -> mPromptSelectionInteractor, () -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt index 25f956574274..a11dace0505c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothLeBroadcast +import android.bluetooth.BluetoothLeBroadcastMetadata import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -50,6 +51,7 @@ class AudioSharingInteractorTest : SysuiTestCase() { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos() @Mock private lateinit var localBluetoothLeBroadcast: LocalBluetoothLeBroadcast + @Mock private lateinit var bluetoothLeBroadcastMetadata: BluetoothLeBroadcastMetadata @Captor private lateinit var callbackCaptor: ArgumentCaptor<BluetoothLeBroadcast.Callback> private lateinit var underTest: AudioSharingInteractor @@ -202,7 +204,7 @@ class AudioSharingInteractorTest : SysuiTestCase() { verify(localBluetoothLeBroadcast) .registerServiceCallBack(any(), callbackCaptor.capture()) runCurrent() - callbackCaptor.value.onPlaybackStarted(0, 0) + callbackCaptor.value.onBroadcastMetadataChanged(0, bluetoothLeBroadcastMetadata) runCurrent() assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isTrue() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt index 97f2e56e6eec..af8c357e6d36 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt @@ -59,6 +59,7 @@ import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.motion.createSysUiComposeMotionTestRule +import com.android.systemui.qs.ui.viewmodel.fakeQsSceneAdapter import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.sceneContainerStartable import com.android.systemui.scene.sceneContainerViewModelFactory @@ -183,6 +184,7 @@ class BouncerPredictiveBackTest : SysuiTestCase() { initialSceneKey = Scenes.Bouncer, overlayByKey = emptyMap(), dataSourceDelegator = kosmos.sceneDataSourceDelegator, + qsSceneAdapter = { kosmos.fakeQsSceneAdapter }, ) } }, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt index 3bf4460e0a4a..94f6769ba406 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt @@ -34,6 +34,11 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.DismissAction +import com.android.systemui.keyguard.shared.model.KeyguardDone +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R @@ -212,6 +217,25 @@ class BouncerSceneContentViewModelTest : SysuiTestCase() { assertThat(isFoldSplitRequired).isTrue() } + @Test + fun onUiDestroyed_clearsPendingDismissAction() = + kosmos.runTest { + val dismissAction by collectLastValue(fakeKeyguardRepository.dismissAction) + fakeKeyguardRepository.setDismissAction( + DismissAction.RunImmediately( + onDismissAction = { KeyguardDone.IMMEDIATE }, + onCancelAction = {}, + message = "", + willAnimateOnLockscreen = true, + ) + ) + assertThat(dismissAction).isNotEqualTo(DismissAction.None) + + underTest.onUiDestroyed() + + assertThat(dismissAction).isEqualTo(DismissAction.None) + } + private fun authMethodsToTest(): List<AuthenticationMethodModel> { return listOf(None, Pin, Password, Pattern, Sim) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt index 06b710eb86eb..b66727e492cf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL import android.app.admin.devicePolicyManager import android.content.Intent import android.content.pm.UserInfo +import android.content.res.mainResources import android.os.UserManager.USER_TYPE_PROFILE_MANAGED import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags @@ -29,6 +30,7 @@ import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_COMMUNAL_HUB +import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2 import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.communal.data.model.DisabledReason @@ -53,7 +55,8 @@ import org.mockito.ArgumentMatchers.eq @SmallTest @RunWith(AndroidJUnit4::class) class CommunalSettingsRepositoryImplTest : SysuiTestCase() { - private val kosmos = testKosmos() + private val kosmos = + testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources } private val testScope = kosmos.testScope private lateinit var underTest: CommunalSettingsRepository @@ -67,6 +70,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { } @EnableFlags(FLAG_COMMUNAL_HUB) + @DisableFlags(FLAG_GLANCEABLE_HUB_V2) @Test fun getFlagEnabled_bothEnabled() { kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) @@ -74,7 +78,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { assertThat(underTest.getFlagEnabled()).isTrue() } - @DisableFlags(FLAG_COMMUNAL_HUB) + @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2) @Test fun getFlagEnabled_bothDisabled() { kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false) @@ -82,7 +86,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { assertThat(underTest.getFlagEnabled()).isFalse() } - @DisableFlags(FLAG_COMMUNAL_HUB) + @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2) @Test fun getFlagEnabled_onlyClassicFlagEnabled() { kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) @@ -91,6 +95,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { } @EnableFlags(FLAG_COMMUNAL_HUB) + @DisableFlags(FLAG_GLANCEABLE_HUB_V2) @Test fun getFlagEnabled_onlyTrunkFlagEnabled() { kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false) @@ -98,6 +103,57 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { assertThat(underTest.getFlagEnabled()).isFalse() } + @EnableFlags(FLAG_GLANCEABLE_HUB_V2) + @DisableFlags(FLAG_COMMUNAL_HUB) + @Test + fun getFlagEnabled_mobileConfigEnabled() { + mContext.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_glanceableHubEnabled, + true, + ) + + assertThat(underTest.getFlagEnabled()).isTrue() + } + + @DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_COMMUNAL_HUB) + @Test + fun getFlagEnabled_onlyMobileConfigEnabled() { + mContext.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_glanceableHubEnabled, + true, + ) + + assertThat(underTest.getFlagEnabled()).isFalse() + } + + @EnableFlags(FLAG_GLANCEABLE_HUB_V2) + @DisableFlags(FLAG_COMMUNAL_HUB) + @Test + fun getFlagEnabled_onlyMobileFlagEnabled() { + mContext.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_glanceableHubEnabled, + false, + ) + + assertThat(underTest.getFlagEnabled()).isFalse() + } + + @EnableFlags(FLAG_GLANCEABLE_HUB_V2) + @DisableFlags(FLAG_COMMUNAL_HUB) + @Test + fun getFlagEnabled_oldFlagIgnored() { + // New config flag enabled. + mContext.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_glanceableHubEnabled, + true, + ) + + // Old config flag disabled. + kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false) + + assertThat(underTest.getFlagEnabled()).isTrue() + } + @EnableFlags(FLAG_COMMUNAL_HUB) @Test fun secondaryUserIsInvalid() = @@ -134,7 +190,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { kosmos.fakeSettings.putIntForUser( Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, - PRIMARY_USER.id + PRIMARY_USER.id, ) val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER)) assertThat(enabledState?.enabled).isFalse() @@ -143,14 +199,14 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { kosmos.fakeSettings.putIntForUser( Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, - SECONDARY_USER.id + SECONDARY_USER.id, ) assertThat(enabledState?.enabled).isFalse() kosmos.fakeSettings.putIntForUser( Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, - PRIMARY_USER.id + PRIMARY_USER.id, ) assertThat(enabledState?.enabled).isTrue() } @@ -201,7 +257,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { kosmos.fakeSettings.putIntForUser( Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, - PRIMARY_USER.id + PRIMARY_USER.id, ) setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL) @@ -228,7 +284,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { kosmos.fakeSettings.putIntForUser( GLANCEABLE_HUB_BACKGROUND_SETTING, type.value, - PRIMARY_USER.id + PRIMARY_USER.id, ) assertWithMessage( "Expected $type when $GLANCEABLE_HUB_BACKGROUND_SETTING is set to" + @@ -253,12 +309,6 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() { UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN) val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0) val WORK_PROFILE = - UserInfo( - 10, - "work", - /* iconPath= */ "", - /* flags= */ 0, - USER_TYPE_PROFILE_MANAGED, - ) + UserInfo(10, "work", /* iconPath= */ "", /* flags= */ 0, USER_TYPE_PROFILE_MANAGED) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index 5bbd3ffc625a..18cc8bf5f0d3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -172,16 +172,16 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } @Test - fun selectedKey_onReorderWidgets_isCleared() = + fun selectedKey_onReorderWidgets_isSet() = testScope.runTest { val selectedKey by collectLastValue(underTest.selectedKey) + underTest.setSelectedKey(null) + assertThat(selectedKey).isNull() + val key = CommunalContentModel.KEY.widget(123) - underTest.setSelectedKey(key) + underTest.onReorderWidgetStart(key) assertThat(selectedKey).isEqualTo(key) - - underTest.onReorderWidgetStart() - assertThat(selectedKey).isNull() } @Test @@ -234,7 +234,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { @Test fun reorderWidget_uiEventLogging_start() { - underTest.onReorderWidgetStart() + underTest.onReorderWidgetStart(CommunalContentModel.KEY.widget(123)) verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorImplTest.kt new file mode 100644 index 000000000000..e1344caa8677 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorImplTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.domain.interactor + +import android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR +import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR +import android.view.layoutInflater +import android.view.windowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository +import com.android.systemui.display.shared.model.DisplayWindowProperties +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class DisplayWindowPropertiesInteractorImplTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val repo = kosmos.fakeDisplayWindowPropertiesRepository + + private val underTest = kosmos.displayWindowPropertiesInteractor + + @Test + fun getForStatusBar_returnsPropertiesWithCorrectWindowType() { + val displayId = 123 + val statusBarWindowProperties = createDisplayWindowProperties(displayId, TYPE_STATUS_BAR) + val navBarWindowProperties = createDisplayWindowProperties(displayId, TYPE_NAVIGATION_BAR) + repo.insert(statusBarWindowProperties) + repo.insert(navBarWindowProperties) + + assertThat(underTest.getForStatusBar(displayId)).isEqualTo(statusBarWindowProperties) + } + + private fun createDisplayWindowProperties(displayId: Int, windowType: Int) = + DisplayWindowProperties( + displayId, + windowType, + context, + kosmos.windowManager, + kosmos.layoutInflater, + ) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java index ba578a39b558..bbd78b317560 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java @@ -35,7 +35,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Matchers.anyObject; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; @@ -315,6 +315,6 @@ public class DozeScreenStateTest extends SysuiTestCase { public void authCallbackRemovedOnDestroy() { mScreen.destroy(); - verify(mAuthController).removeCallback(anyObject()); + verify(mAuthController).removeCallback(any()); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt index dd8370231ef0..e288522ec212 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt @@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.BACK +import com.android.systemui.contextualeducation.GestureType.HOME import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor import com.android.systemui.education.domain.interactor.contextualEducationInteractor @@ -52,6 +53,7 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.any +import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -66,6 +68,7 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { private val minDurationForNextEdu = KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds private lateinit var underTest: ContextualEduUiCoordinator + private lateinit var previousDialog: Dialog @Mock private lateinit var dialog: Dialog @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper @@ -95,9 +98,11 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { kosmos.applicationCoroutineScope, viewModel, kosmos.applicationContext, - notificationManager + notificationManager, ) { model -> toastContent = model.message + previousDialog = dialog + dialog = mock<Dialog>() dialog } underTest.start() @@ -129,6 +134,14 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { } @Test + fun dismissPreviousDialogOnNewDialog() = + testScope.runTest { + triggerEducation(BACK) + triggerEducation(HOME) + verify(previousDialog).dismiss() + } + + @Test fun verifyBackEduToastContent() = testScope.runTest { triggerEducation(BACK) @@ -149,14 +162,14 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { verifyNotificationContent( R.string.back_edu_notification_title, R.string.back_edu_notification_content, - notificationCaptor.value + notificationCaptor.value, ) } private fun verifyNotificationContent( titleResId: Int, contentResId: Int, - notification: Notification + notification: Notification, ) { val expectedContent = context.getString(contentResId) val expectedTitle = context.getString(titleResId) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt index 715d907b7372..c9f703533e89 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt @@ -17,21 +17,28 @@ package com.android.systemui.keyboard.shortcut.data.source import android.content.res.mainResources +import android.hardware.input.KeyGlyphMap +import android.hardware.input.fakeInputManager +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.view.KeyEvent.KEYCODE_EMOJI_PICKER import android.view.KeyboardShortcutGroup import android.view.WindowManager.KeyboardShortcutsReceiver import android.view.mockWindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_SHORTCUT_HELPER_KEY_GLYPH import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @@ -41,7 +48,8 @@ class InputShortcutsSourceTest : SysuiTestCase() { private val testScope = kosmos.testScope private val mockWindowManager = kosmos.mockWindowManager - private val source = InputShortcutsSource(kosmos.mainResources, mockWindowManager) + private val inputManager = kosmos.fakeInputManager.inputManager + private val source = InputShortcutsSource(kosmos.mainResources, mockWindowManager, inputManager) private var wmImeShortcutGroups: List<KeyboardShortcutGroup>? = null @@ -88,6 +96,36 @@ class InputShortcutsSourceTest : SysuiTestCase() { assertThat(groups).hasSize(4) } + @Test + @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH) + fun shortcutGroups_flagEnabled_inputManagerReturnsKeyGlyph_returnsEmojiShortcut() = + testScope.runTest { + val mockKeyGlyph = mock(KeyGlyphMap::class.java) + whenever(mockKeyGlyph.functionRowKeys).thenReturn(intArrayOf(KEYCODE_EMOJI_PICKER)) + whenever(inputManager.getKeyGlyphMap(TEST_DEVICE_ID)).thenReturn(mockKeyGlyph) + wmImeShortcutGroups = null + + val groups = source.shortcutGroups(TEST_DEVICE_ID) + + val keyCodes = groups[0].items.map { it.keycode } + assertThat(keyCodes).contains(KEYCODE_EMOJI_PICKER) + } + + @Test + @DisableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH) + fun shortcutGroups_flagDisabled_inputManagerReturnsKeyGlyph_returnsNoEmojiShortcut() = + testScope.runTest { + val mockKeyGlyph = mock(KeyGlyphMap::class.java) + whenever(mockKeyGlyph.functionRowKeys).thenReturn(intArrayOf(KEYCODE_EMOJI_PICKER)) + whenever(inputManager.getKeyGlyphMap(TEST_DEVICE_ID)).thenReturn(mockKeyGlyph) + wmImeShortcutGroups = null + + val groups = source.shortcutGroups(TEST_DEVICE_ID) + + val keyCodes = groups[0].items.map { it.keycode } + assertThat(keyCodes).doesNotContain(KEYCODE_EMOJI_PICKER) + } + companion object { private const val TEST_DEVICE_ID = 1234 } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSourceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSourceTest.kt new file mode 100644 index 000000000000..495e98d0edfb --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSourceTest.kt @@ -0,0 +1,138 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.data.source + +import android.content.res.mainResources +import android.hardware.input.KeyGlyphMap +import android.hardware.input.KeyGlyphMap.KeyCombination +import android.hardware.input.fakeInputManager +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.view.KeyEvent +import android.view.KeyEvent.KEYCODE_BACK +import android.view.KeyEvent.KEYCODE_HOME +import android.view.KeyEvent.KEYCODE_RECENT_APPS +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_SHORTCUT_HELPER_KEY_GLYPH +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class SystemShortcutsSourceTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + + private val inputManager = kosmos.fakeInputManager.inputManager + private val source = SystemShortcutsSource(kosmos.mainResources, inputManager) + private val mockKeyGlyphMap = mock(KeyGlyphMap::class.java) + private val functionRowKeyCodes = listOf(KEYCODE_HOME, KEYCODE_BACK, KEYCODE_RECENT_APPS) + + @Before + fun setUp() { + whenever(mockKeyGlyphMap.functionRowKeys).thenReturn(intArrayOf()) + whenever(inputManager.getKeyGlyphMap(TEST_DEVICE_ID)).thenReturn(mockKeyGlyphMap) + } + + @Test + @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH) + fun shortcutGroups_flagEnabled_inputManagerReturnsNoFunctionRowKeys_returnsNoFunctionRowShortcuts() = + testScope.runTest { + val groups = source.shortcutGroups(TEST_DEVICE_ID) + + val keyCodes = groups[0].items.map { it.keycode } + assertThat(keyCodes).containsNoneIn(functionRowKeyCodes) + } + + @Test + @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH) + fun shortcutGroups_flagEnabled_inputManagerReturnsFunctionRowKeys_returnsFunctionRowShortcuts() = + testScope.runTest { + whenever(mockKeyGlyphMap.functionRowKeys) + .thenReturn(intArrayOf(KEYCODE_HOME, KEYCODE_BACK, KEYCODE_RECENT_APPS)) + + val groups = source.shortcutGroups(TEST_DEVICE_ID) + + val keyCodes = groups[0].items.map { it.keycode } + assertThat(keyCodes).containsAtLeastElementsIn(functionRowKeyCodes) + } + + @Test + @DisableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH) + fun shortcutGroups_flagDisabled_inputManagerReturnsNoFunctionRowKeys_returnsDefaultFunctionRowShortcuts() = + testScope.runTest { + val groups = source.shortcutGroups(TEST_DEVICE_ID) + + val keyCodes = groups[0].items.map { it.keycode } + assertThat(keyCodes).containsAtLeastElementsIn(functionRowKeyCodes) + } + + @Test + @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH) + fun shortcutGroups_flagEnabled_inputManagerReturnsHardwareShortcuts_returnsHardwareShortcuts() = + testScope.runTest { + whenever(mockKeyGlyphMap.functionRowKeys).thenReturn(intArrayOf()) + val hardwareShortcutMap = + mapOf(Pair(KeyCombination(KeyEvent.META_META_ON, KeyEvent.KEYCODE_1), KEYCODE_BACK)) + whenever(mockKeyGlyphMap.hardwareShortcuts).thenReturn(hardwareShortcutMap) + + val groups = source.shortcutGroups(TEST_DEVICE_ID) + + val shortcuts = groups[0].items.map { c -> Triple(c.label, c.modifiers, c.keycode) } + val hardwareShortcut = + Triple( + context.getString(R.string.group_system_go_back), + KeyEvent.META_META_ON, + KeyEvent.KEYCODE_1, + ) + assertThat(shortcuts).contains(hardwareShortcut) + } + + @Test + @DisableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH) + fun shortcutGroups_flagDisabled_inputManagerReturnsHardwareShortcuts_returnsNoHardwareShortcuts() = + testScope.runTest { + val hardwareShortcutMap = + mapOf(Pair(KeyCombination(KeyEvent.META_META_ON, KeyEvent.KEYCODE_1), KEYCODE_BACK)) + whenever(mockKeyGlyphMap.hardwareShortcuts).thenReturn(hardwareShortcutMap) + + val groups = source.shortcutGroups(TEST_DEVICE_ID) + + val shortcuts = groups[0].items.map { c -> Triple(c.label, c.modifiers, c.keycode) } + val hardwareShortcut = + Triple( + context.getString(R.string.group_system_go_back), + KeyEvent.META_META_ON, + KeyEvent.KEYCODE_1, + ) + assertThat(shortcuts).doesNotContain(hardwareShortcut) + } + + companion object { + private const val TEST_DEVICE_ID = 1234 + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt index 8466eab2aca6..6d22b4903920 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt @@ -87,6 +87,10 @@ object TestShortcuts { key("Shift") key("M") } + contentDescription { + "${shortcutInfoWithRepeatedLabel.label}, " + + "Press key Meta plus H, or Meta plus L, or Shift plus M" + } } private val goHomeShortcutInfo = @@ -109,6 +113,7 @@ object TestShortcuts { key(R.drawable.ic_ksh_key_meta) key("N") } + contentDescription { "${standardShortcutInfo1.label}, Press key Meta plus N" } } private val customGoHomeShortcut = @@ -119,6 +124,7 @@ object TestShortcuts { key("A") isCustom(true) } + contentDescription { "Go to home screen, Press key Ctrl plus Alt plus A" } } private val standardShortcutInfo2 = @@ -135,6 +141,7 @@ object TestShortcuts { key("Shift") key("Z") } + contentDescription { "${standardShortcutInfo2.label}, Press key Alt plus Shift plus Z" } } private val standardShortcutInfo3 = @@ -150,6 +157,7 @@ object TestShortcuts { key("Ctrl") key("J") } + contentDescription { "${standardShortcutInfo3.label}, Press key Ctrl plus J" } } private val shortcutInfoWithUnsupportedModifiers = @@ -205,6 +213,7 @@ object TestShortcuts { key("Ctrl") key("Space") } + contentDescription { "Switch to next language, Press key Ctrl plus Space" } } private val switchToPreviousLanguageShortcut = @@ -214,6 +223,9 @@ object TestShortcuts { key("Shift") key("Space") } + contentDescription { + "Switch to previous language, Press key Ctrl plus Shift plus Space" + } } private val subCategoryForInputLanguageSwitchShortcuts = @@ -292,6 +304,10 @@ object TestShortcuts { key("A") isCustom(true) } + contentDescription { + "Go to home screen, Press key Ctrl plus Alt plus B, " + + "or Ctrl plus Alt plus A" + } } ), ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt index f7c77017e239..c3cedba65340 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt @@ -333,6 +333,42 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { } } + @Test + fun categories_addsSameContentDescriptionForShortcutsOfSameType() { + testScope.runTest { + setCustomInputGestures(listOf(customInputGestureTypeHome)) + systemShortcutsSource.setGroups(groupWithGoHomeShortcutInfo) + helper.showFromActivity() + + val categories by collectLastValue(interactor.shortcutCategories) + val contentDescriptions = + categories?.flatMap { + it.subCategories.flatMap { it.shortcuts.map { it.contentDescription } } + } + + assertThat(contentDescriptions) + .containsExactly( + "Go to home screen, Press key Ctrl plus Alt plus B, or Ctrl plus Alt plus A", + "Standard shortcut 3, Press key Ctrl plus J", + "Standard shortcut 2, Press key Alt plus Shift plus Z", + "Standard shortcut 1, Press key Meta plus N", + "Standard shortcut 1, Press key Meta plus N", + "Standard shortcut 2, Press key Alt plus Shift plus Z", + "Standard shortcut 3, Press key Ctrl plus J", + "Switch to next language, Press key Ctrl plus Space", + "Switch to previous language, Press key Ctrl plus Shift plus Space", + "Standard shortcut 1, Press key Meta plus N", + "Standard shortcut 2, Press key Alt plus Shift plus Z", + "Standard shortcut 3, Press key Ctrl plus J", + "Standard shortcut 3, Press key Ctrl plus J", + "Standard shortcut 2, Press key Alt plus Shift plus Z", + "Standard shortcut 1, Press key Meta plus N", + "Standard shortcut 2, Press key Alt plus Shift plus Z", + "Standard shortcut 1, Press key Meta plus N", + ) + } + } + private fun setCustomInputGestures(customInputGestures: List<InputGestureData>) { whenever(fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull())) .thenReturn(customInputGestures) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt index d0ce34c2d68d..d0ce34c2d68d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt index e1496879a34b..dadcf71a4723 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt @@ -35,10 +35,9 @@ import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.shared.model.DismissAction import com.android.systemui.keyguard.shared.model.KeyguardDone import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope -import com.android.systemui.power.data.repository.fakePowerRepository -import com.android.systemui.power.shared.model.WakeSleepReason -import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setSceneTransition @@ -222,28 +221,6 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { } @Test - fun resetDismissAction() = - testScope.runTest { - kosmos.setSceneTransition(Idle(Scenes.Bouncer)) - var wasOnCancelInvoked = false - startInteractor() - keyguardRepository.setDismissAction( - DismissAction.RunAfterKeyguardGone( - dismissAction = {}, - onCancelAction = { wasOnCancelInvoked = true }, - message = "message", - willAnimateOnLockscreen = true, - ) - ) - assertThat(wasOnCancelInvoked).isFalse() - kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) - runCurrent() - - assertThat(wasOnCancelInvoked).isTrue() - assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None) - } - - @Test fun doNotResetDismissActionOnUnlockedShade() = testScope.runTest { kosmos.setSceneTransition(Idle(Scenes.Bouncer)) @@ -272,37 +249,6 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { } @Test - fun resetDismissAction_onBouncer_OnAsleep() = - testScope.runTest { - kosmos.setSceneTransition(Idle(Scenes.Bouncer)) - kosmos.fakeAuthenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.None - ) - var wasOnCancelInvoked = false - startInteractor() - - keyguardRepository.setDismissAction( - DismissAction.RunAfterKeyguardGone( - dismissAction = {}, - onCancelAction = { wasOnCancelInvoked = true }, - message = "message", - willAnimateOnLockscreen = true, - ) - ) - assertThat(wasOnCancelInvoked).isFalse() - kosmos.fakePowerRepository.updateWakefulness( - rawState = WakefulnessState.ASLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.TIMEOUT, - powerButtonLaunchGestureTriggered = false, - ) - runCurrent() - - assertThat(wasOnCancelInvoked).isTrue() - assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None) - } - - @Test fun setDismissAction_callsCancelRunnableOnPreviousDismissAction() = testScope.runTest { val dismissAction by collectLastValue(keyguardRepository.dismissAction) @@ -410,6 +356,25 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { assertThat(wasCancelActionInvoked).isFalse() } + @Test + fun clearDismissAction() = + kosmos.runTest { + val dismissAction by collectLastValue(fakeKeyguardRepository.dismissAction) + fakeKeyguardRepository.setDismissAction( + DismissAction.RunImmediately( + onDismissAction = { KeyguardDone.IMMEDIATE }, + onCancelAction = {}, + message = "", + willAnimateOnLockscreen = true, + ) + ) + assertThat(dismissAction).isNotEqualTo(DismissAction.None) + + underTest.clearDismissAction() + + assertThat(dismissAction).isEqualTo(DismissAction.None) + } + private fun TestScope.startInteractor() { testScope.backgroundScope.launchTraced( "KeyguardDismissActionInteractorTest#startInteractor" diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt index bd26e4205242..bef995fa5225 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.widget.lockPatternUtils import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.biometricSettingsRepository @@ -33,6 +34,8 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @@ -52,14 +55,21 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() { testScope.runTest { val values by collectValues(underTest.lockWhileAwakeEvents) - underTest.onKeyguardServiceDoKeyguardTimeout(options = null) + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + runCurrent() + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout( + options = null + ) runCurrent() assertThat(values) .containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON) advanceTimeBy(1000) - underTest.onKeyguardServiceDoKeyguardTimeout(options = null) + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout( + options = null + ) runCurrent() assertThat(values) @@ -69,8 +79,15 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() { ) } + /** + * We re-show keyguard when it's re-enabled, but only if it was originally showing when we + * disabled it. + * + * If it wasn't showing when originally disabled it, re-enabling it should do nothing (the + * keyguard will re-show next time we're locked). + */ @Test - fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() = + fun emitsWhenKeyguardReenabled_onlyIfShowingWhenDisabled() = testScope.runTest { val values by collectValues(underTest.lockWhileAwakeEvents) @@ -98,4 +115,49 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() { assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED) } + + /** + * Un-suppressing keyguard should never cause us to re-show. We'll re-show when we're next + * locked, even if we were showing when originally suppressed. + */ + @Test + fun doesNotEmit_keyguardNoLongerSuppressed() = + testScope.runTest { + val values by collectValues(underTest.lockWhileAwakeEvents) + + // Enable keyguard and then suppress it. + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) + runCurrent() + + assertEquals(0, values.size) + + // Un-suppress keyguard. + whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + runCurrent() + + assertEquals(0, values.size) + } + + /** + * Lockdown and lockNow() should not cause us to lock while awake if we are suppressed via adb. + */ + @Test + fun doesNotEmit_fromLockdown_orFromLockNow_ifEnabledButSuppressed() = + testScope.runTest { + val values by collectValues(underTest.lockWhileAwakeEvents) + + // Set keyguard enabled, but then disable lockscreen (suppress it). + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) + runCurrent() + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null) + runCurrent() + + kosmos.biometricSettingsRepository.setIsUserInLockdown(true) + runCurrent() + + assertEquals(0, values.size) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt index 7e249e8c179d..ead151ee6df2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt @@ -87,9 +87,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) repository.setKeyguardEnabled(false) @@ -100,33 +100,26 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { false, // Default to false. true, // True now that keyguard service is disabled ), - canWake + canWake, ) repository.setKeyguardEnabled(true) runCurrent() - assertEquals( - listOf( - false, - true, - false, - ), - canWake - ) + assertEquals(listOf(false, true, false), canWake) } @Test @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) - fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled() = + fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_onlyAfterWakefulnessChange() = testScope.runTest { val canWake by collectValues(underTest.canWakeDirectlyToGone) assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) @@ -136,9 +129,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { listOf( // Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to // update on the next wake/sleep event. - false, + false ), - canWake + canWake, ) kosmos.powerInteractor.setAsleepForTest() @@ -150,7 +143,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // True since we slept after setting isLockScreenDisabled=true true, ), - canWake + canWake, ) kosmos.powerInteractor.setAwakeForTest() @@ -159,25 +152,75 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { kosmos.powerInteractor.setAsleepForTest() runCurrent() + assertEquals(listOf(false, true), canWake) + + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + kosmos.powerInteractor.setAwakeForTest() + runCurrent() + + assertEquals(listOf(false, true, false), canWake) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_lockNowEvent() = + testScope.runTest { + val canWake by collectValues(underTest.canWakeDirectlyToGone) + + assertEquals( + listOf( + false // Defaults to false. + ), + canWake, + ) + + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) + runCurrent() + + assertEquals( + listOf( + // Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to + // update on the next wakefulness or lockNow event. + false + ), + canWake, + ) + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null) + runCurrent() + assertEquals( listOf( false, + // True when lockNow() called after setting isLockScreenDisabled=true true, ), - canWake + canWake, ) whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) - kosmos.powerInteractor.setAwakeForTest() + runCurrent() + + assertEquals( + listOf( + false, + // Still true since no lockNow() calls made. + true, + ), + canWake, + ) + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null) runCurrent() assertEquals( listOf( false, true, + // False again after the lockNow() call. false, ), - canWake + canWake, ) } @@ -189,9 +232,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) repository.setBiometricUnlockState(BiometricUnlockMode.WAKE_AND_UNLOCK) @@ -213,9 +256,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) repository.setCanIgnoreAuthAndReturnToGone(true) @@ -237,9 +280,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt())) @@ -257,13 +300,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { ) runCurrent() - assertEquals( - listOf( - false, - true, - ), - canWake - ) + assertEquals(listOf(false, true), canWake) verify(kosmos.alarmManager) .setExactAndAllowWhileIdle( @@ -281,9 +318,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt())) @@ -312,7 +349,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // Timed out, so we can ignore auth/return to GONE. true, ), - canWake + canWake, ) verify(kosmos.alarmManager) @@ -338,7 +375,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // alarm in flight that should be canceled. false, ), - canWake + canWake, ) kosmos.powerInteractor.setAsleepForTest( @@ -354,25 +391,17 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // Back to sleep. true, ), - canWake + canWake, ) // Simulate the first sleep's alarm coming in. lastRegisteredBroadcastReceiver?.onReceive( kosmos.mockedContext, - Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD") + Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD"), ) runCurrent() // It should not have any effect. - assertEquals( - listOf( - false, - true, - false, - true, - ), - canWake - ) + assertEquals(listOf(false, true, false, true), canWake) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index e60a52c12c0b..21019875f51e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -55,6 +55,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @@ -886,6 +887,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer + @Ignore("b/378766637") fun lockscreenVisibilityWithScenes() = testScope.runTest { val isDeviceUnlocked by diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 63ec78fd9ee5..63ec78fd9ee5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java index 646722bee35f..555ba56e087f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java @@ -90,6 +90,7 @@ import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavBarHelper; import com.android.systemui.navigationbar.NavigationBarController; @@ -116,8 +117,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.data.repository.LightBarControllerStore; -import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.AutoHideControllerStore; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LightBarTransitionsController; @@ -148,6 +148,7 @@ import java.util.concurrent.Executor; @SmallTest public class NavigationBarTest extends SysuiTestCase { private static final int EXTERNAL_DISPLAY_ID = 2; + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); private NavigationBar mNavigationBar; private NavigationBar mExternalDisplayNavigationBar; @@ -209,11 +210,7 @@ public class NavigationBarTest extends SysuiTestCase { @Mock private LightBarController mLightBarController; @Mock - private LightBarControllerStore mLightBarControllerStore; - @Mock - private AutoHideController mAutoHideController; - @Mock - private AutoHideController.Factory mAutoHideControllerFactory; + private LightBarController.Factory mLightBarcontrollerFactory; @Mock private WindowManager mWindowManager; @Mock @@ -248,6 +245,8 @@ public class NavigationBarTest extends SysuiTestCase { private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake(); private TaskStackChangeListeners mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance(); + private final AutoHideControllerStore mAutoHideControllerStore = + mKosmos.getAutoHideControllerStore(); @Rule public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck(); @@ -258,8 +257,7 @@ public class NavigationBarTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); - when(mLightBarControllerStore.forDisplay(anyInt())).thenReturn(mLightBarController); - when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController); + when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController); when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton); when(mNavigationBarView.getRecentsButton()).thenReturn(mRecentsButton); when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton); @@ -650,9 +648,9 @@ public class NavigationBarTest extends SysuiTestCase { mFakeExecutor, mUiEventLogger, mNavBarHelper, - mLightBarControllerStore, - mAutoHideController, - mAutoHideControllerFactory, + mLightBarController, + mLightBarcontrollerFactory, + mAutoHideControllerStore, Optional.of(mTelecomManager), mInputMethodManager, mDeadZone, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt index b5fc52f7a8d6..87e2fefde989 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt @@ -16,11 +16,13 @@ package com.android.systemui.qs.composefragment.viewmodel +import androidx.compose.runtime.snapshots.Snapshot import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.testing.TestLifecycleOwner import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn @@ -36,7 +38,6 @@ import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain -import org.junit.After import org.junit.Before import org.junit.runner.RunWith @@ -58,11 +59,14 @@ abstract class AbstractQSFragmentComposeViewModelTest : SysuiTestCase() { @Before fun setUp() { Dispatchers.setMain(kosmos.testDispatcher) - } + onTeardown { Dispatchers.resetMain() } - @After - fun teardown() { - Dispatchers.resetMain() + val globalWriteObserverHandle = + Snapshot.registerGlobalWriteObserver { + Snapshot.sendApplyNotifications() + kosmos.runCurrent() + } + onTeardown { globalWriteObserverHandle.dispose() } } protected inline fun TestScope.testWithinLifecycle( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt index 921a8a610c37..111b3b65c05d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt @@ -20,7 +20,6 @@ import android.app.StatusBarManager import android.content.testableContext import android.graphics.Rect import android.testing.TestableLooper.RunWithLooper -import androidx.compose.runtime.snapshots.Snapshot import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository @@ -186,15 +185,12 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() val squishiness by collectLastValue(tileSquishinessInteractor.squishiness) underTest.squishinessFraction = 0.3f - Snapshot.sendApplyNotifications() assertThat(squishiness).isWithin(epsilon).of(0.3f.constrainSquishiness()) underTest.squishinessFraction = 0f - Snapshot.sendApplyNotifications() assertThat(squishiness).isWithin(epsilon).of(0f.constrainSquishiness()) underTest.squishinessFraction = 1f - Snapshot.sendApplyNotifications() assertThat(squishiness).isWithin(epsilon).of(1f.constrainSquishiness()) } } @@ -328,13 +324,9 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() setMediaState(ACTIVE_MEDIA) setConfigurationForMediaInRow(mediaInRow = false) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED) setConfigurationForMediaInRow(mediaInRow = true) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.COLLAPSED) } } @@ -347,13 +339,9 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() setMediaState(ACTIVE_MEDIA) setConfigurationForMediaInRow(mediaInRow = false) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED) setConfigurationForMediaInRow(mediaInRow = true) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED) } } @@ -366,13 +354,9 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() setMediaState(ACTIVE_MEDIA) setCollapsedMediaInLandscape(false) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED) setCollapsedMediaInLandscape(true) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.COLLAPSED) } } @@ -401,13 +385,11 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() underTest.squishinessFraction = 0.3f underTest.shouldUpdateSquishinessOnMedia = true - Snapshot.sendApplyNotifications() runCurrent() assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(0.3f) underTest.shouldUpdateSquishinessOnMedia = false - Snapshot.sendApplyNotifications() runCurrent() assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(1f) } @@ -421,20 +403,15 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() underTest.squishinessFraction = 0.3f sysuiStatusBarStateController.setState(StatusBarState.SHADE) - Snapshot.sendApplyNotifications() runCurrent() assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(0.3f) sysuiStatusBarStateController.setState(StatusBarState.KEYGUARD) runCurrent() - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f) sysuiStatusBarStateController.setState(StatusBarState.SHADE_LOCKED) runCurrent() - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f) } } @@ -446,8 +423,6 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() setMediaState(ACTIVE_MEDIA) setConfigurationForMediaInRow(false) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.disappearParameters) .isEqualTo(disappearParamsColumn) @@ -455,8 +430,6 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() .isEqualTo(disappearParamsColumn) setConfigurationForMediaInRow(true) - Snapshot.sendApplyNotifications() - runCurrent() assertThat(underTest.qqsMediaHost.disappearParameters).isEqualTo(disappearParamsRow) assertThat(underTest.qsMediaHost.disappearParameters).isEqualTo(disappearParamsRow) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt new file mode 100644 index 000000000000..98770c724126 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.viewmodel + + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.FakeQSTile +import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository +import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class DetailsViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private lateinit var underTest: DetailsViewModel + private val spec = TileSpec.create("internet") + private val specNoDetails = TileSpec.create("NoDetailsTile") + + @Before + fun setUp() { + underTest = kosmos.detailsViewModel + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun changeTileDetailsViewModel() = with(kosmos) { + testScope.runTest { + val specs = listOf( + spec, + specNoDetails, + ) + tileSpecRepository.setTiles(0, specs) + runCurrent() + + val tiles = currentTilesInteractor.currentTiles.value + + assertThat(currentTilesInteractor.currentTilesSpecs.size).isEqualTo(2) + assertThat(tiles!![1].spec).isEqualTo(specNoDetails) + (tiles!![1].tile as FakeQSTile).hasDetailsViewModel = false + + assertThat(underTest.activeTileDetails).isNull() + + // Click on the tile who has the `spec`. + assertThat(underTest.onTileClicked(spec)).isTrue() + assertThat(underTest.activeTileDetails).isNotNull() + assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet") + + // Click on a tile who dose not have a valid spec. + assertThat(underTest.onTileClicked(null)).isFalse() + assertThat(underTest.activeTileDetails).isNull() + + // Click again on the tile who has the `spec`. + assertThat(underTest.onTileClicked(spec)).isTrue() + assertThat(underTest.activeTileDetails).isNotNull() + assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet") + + // Click on a tile who dose not have a detailed view. + assertThat(underTest.onTileClicked(specNoDetails)).isFalse() + assertThat(underTest.activeTileDetails).isNull() + + underTest.closeDetailedView() + assertThat(underTest.activeTileDetails).isNull() + + assertThat(underTest.onTileClicked(null)).isFalse() + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 8995f46e7a72..165ff7bf08b1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -691,6 +691,32 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { verify(logger, never()).logTileUserChanged(TileSpec.create("a"), 0) } + + @Test + fun getTileDetails() = + testScope.runTest(USER_INFO_0) { + val tiles by collectLastValue(underTest.currentTiles) + val tileA = TileSpec.create("a") + val tileB = TileSpec.create("b") + val tileNoDetails = TileSpec.create("NoDetails") + + val specs = listOf(tileA, tileB, tileNoDetails) + + assertThat(tiles!!.isEmpty()).isTrue() + + tileSpecRepository.setTiles(USER_INFO_0.id, specs) + assertThat(tiles!!.size).isEqualTo(3) + + // The third tile doesn't have a details view. + assertThat(tiles!![2].spec).isEqualTo(tileNoDetails) + (tiles!![2].tile as FakeQSTile).hasDetailsViewModel = false + + assertThat(tiles!![0].tile.detailsViewModel.getTitle()).isEqualTo("a") + assertThat(tiles!![1].tile.detailsViewModel.getTitle()).isEqualTo("b") + assertThat(tiles!![2].tile.detailsViewModel).isNull() + } + + private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) { this.state = state this.label = label @@ -770,7 +796,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private val USER_INFO_0 = UserInfo().apply { id = 0 } private val USER_INFO_1 = UserInfo().apply { id = 1 } - private val VALID_TILES = setOf("a", "b", "c", "d", "e") + private val VALID_TILES = setOf("a", "b", "c", "d", "e", "NoDetails") private val TEST_COMPONENT = ComponentName("pkg", "cls") private val CUSTOM_TILE_SPEC = TileSpec.Companion.create(TEST_COMPONENT) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt index db2297c99315..3d014b6822b4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt @@ -35,9 +35,9 @@ import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.init.NotificationsController import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 5d49c113a539..79bb0c401e78 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -2861,9 +2861,7 @@ class SceneContainerStartableTest : SysuiTestCase() { ) private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean) = - FakeHeadsUpRowRepository(key = key, elementKey = Any()).apply { - this.isPinned.value = isPinned - } + FakeHeadsUpRowRepository(key = key, elementKey = Any(), isPinned = isPinned) private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) { kosmos.fingerprintPropertyRepository.setProperties( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index ed31f366e1d0..0d8d57e52dbf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -150,7 +150,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ConversationNotificationManager; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger; import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository; @@ -178,12 +178,12 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager; import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; @@ -245,7 +245,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected NotificationPanelView mView; @Mock protected LayoutInflater mLayoutInflater; @Mock protected DynamicPrivacyController mDynamicPrivacyController; - @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock protected ShadeTouchableRegionManager mShadeTouchableRegionManager; @Mock protected KeyguardStateController mKeyguardStateController; @Mock protected DozeLog mDozeLog; private final ShadeLogger mShadeLog = new ShadeLogger(logcatLogBuffer()); @@ -703,7 +703,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mMetricsLogger, mShadeLog, mConfigurationController, - () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + () -> flingAnimationUtilsBuilder, mShadeTouchableRegionManager, mConversationNotificationManager, mMediaHierarchyManager, mStatusBarKeyguardViewManager, mGutsManager, @@ -819,7 +819,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mLockscreenShadeTransitionController, mNotificationShadeDepthController, mShadeHeaderController, - mStatusBarTouchableRegionManager, + mShadeTouchableRegionManager, () -> mStatusBarLongPressGestureDetector, mKeyguardStateController, mKeyguardBypassController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java index 61d4c9968d49..b58c13c34505 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java @@ -71,8 +71,8 @@ import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; @@ -125,7 +125,7 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { @Mock protected LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Mock protected NotificationShadeDepthController mNotificationShadeDepthController; @Mock protected ShadeHeaderController mShadeHeaderController; - @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock protected ShadeTouchableRegionManager mShadeTouchableRegionManager; @Mock protected StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector; @Mock protected DozeParameters mDozeParameters; @Mock protected KeyguardStateController mKeyguardStateController; @@ -250,7 +250,7 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { mLockscreenShadeTransitionController, mNotificationShadeDepthController, mShadeHeaderController, - mStatusBarTouchableRegionManager, + mShadeTouchableRegionManager, () -> mStatusBarLongPressGestureDetector, mKeyguardStateController, mKeyguardBypassController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 0f476d0a0455..c6ce58185cf0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -36,10 +36,10 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt new file mode 100644 index 000000000000..096675962d80 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.data.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.testScope +import com.android.systemui.shade.display.ShadeDisplayPolicy +import com.android.systemui.shade.display.SpecificDisplayIdPolicy +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ShadeDisplaysRepositoryTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val defaultPolicy = SpecificDisplayIdPolicy(0) + + private val shadeDisplaysRepository = + ShadeDisplaysRepositoryImpl(defaultPolicy, testScope.backgroundScope) + + @Test + fun policy_changing_propagatedFromTheLatestPolicy() = + testScope.runTest { + val displayIds by collectValues(shadeDisplaysRepository.displayId) + val policy1 = MutablePolicy() + val policy2 = MutablePolicy() + + assertThat(displayIds).containsExactly(0) + + shadeDisplaysRepository.policy.value = policy1 + + policy1.sendDisplayId(1) + + assertThat(displayIds).containsExactly(0, 1) + + policy1.sendDisplayId(2) + + assertThat(displayIds).containsExactly(0, 1, 2) + + shadeDisplaysRepository.policy.value = policy2 + + assertThat(displayIds).containsExactly(0, 1, 2, 0) + + policy1.sendDisplayId(4) + + // Changes to the first policy don't affect the output now + assertThat(displayIds).containsExactly(0, 1, 2, 0) + + policy2.sendDisplayId(5) + + assertThat(displayIds).containsExactly(0, 1, 2, 0, 5) + } + + private class MutablePolicy : ShadeDisplayPolicy { + fun sendDisplayId(id: Int) { + _displayId.value = id + } + + private val _displayId = MutableStateFlow(0) + override val name: String + get() = "mutable_policy" + + override val displayId: StateFlow<Int> + get() = _displayId + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt index af01547be7e3..d584dc9ceef2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt @@ -23,12 +23,17 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.shade.ShadePrimaryDisplayCommand +import com.android.systemui.shade.display.ShadeDisplayPolicy import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.testKosmos +import com.google.common.truth.StringSubject import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -37,15 +42,26 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class ShadePrimaryDisplayCommandTest : SysuiTestCase() { - private val kosmos = testKosmos() + private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope private val commandRegistry = kosmos.commandRegistry private val displayRepository = kosmos.displayRepository - private val shadeDisplaysRepository = ShadeDisplaysRepositoryImpl() + private val defaultPolicy = kosmos.defaultShadeDisplayPolicy + private val policy1 = makePolicy("policy_1") + private val shadeDisplaysRepository = kosmos.shadeDisplaysRepository private val pw = PrintWriter(StringWriter()) + private val policies = + setOf(defaultPolicy, policy1, makePolicy("policy_2"), makePolicy("policy_3")) + private val underTest = - ShadePrimaryDisplayCommand(commandRegistry, displayRepository, shadeDisplaysRepository) + ShadePrimaryDisplayCommand( + commandRegistry, + displayRepository, + shadeDisplaysRepository, + policies, + defaultPolicy, + ) @Before fun setUp() { @@ -96,4 +112,41 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() { assertThat(displayId).isEqualTo(newDisplayId) } + + @Test + fun policies_listsAllPolicies() = + testScope.runTest { + val stringWriter = StringWriter() + commandRegistry.onShellCommand( + PrintWriter(stringWriter), + arrayOf("shade_display_override", "policies"), + ) + val result = stringWriter.toString() + + assertThat(result).containsAllIn(policies.map { it.name }) + } + + @Test + fun policies_setsSpecificPolicy() = + testScope.runTest { + val policy by collectLastValue(shadeDisplaysRepository.policy) + + commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", policy1.name)) + + assertThat(policy!!.name).isEqualTo(policy1.name) + } + + private fun makePolicy(policyName: String): ShadeDisplayPolicy { + return object : ShadeDisplayPolicy { + override val name: String + get() = policyName + + override val displayId: StateFlow<Int> + get() = MutableStateFlow(0) + } + } +} + +private fun StringSubject.containsAllIn(strings: List<String>) { + strings.forEach { contains(it) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt new file mode 100644 index 000000000000..4d4efd11f3a8 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.display + +import android.view.Display +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.display.data.repository.display +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.test.runTest +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class AnyExternalShadeDisplayPolicyTest : SysuiTestCase() { + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + private val testScope = kosmos.testScope + private val displayRepository = kosmos.displayRepository + val underTest = AnyExternalShadeDisplayPolicy(displayRepository, testScope.backgroundScope) + + @Test + fun displayId_ignoresUnwantedTypes() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays( + display(id = 0, type = Display.TYPE_INTERNAL), + display(id = 1, type = Display.TYPE_UNKNOWN), + display(id = 2, type = Display.TYPE_VIRTUAL), + display(id = 3, type = Display.TYPE_EXTERNAL), + ) + + assertThat(displayId).isEqualTo(3) + } + + @Test + fun displayId_onceRemoved_goesToNextDisplay() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays( + display(id = 0, type = Display.TYPE_INTERNAL), + display(id = 2, type = Display.TYPE_EXTERNAL), + display(id = 3, type = Display.TYPE_EXTERNAL), + ) + + assertThat(displayId).isEqualTo(2) + + displayRepository.removeDisplay(2) + + assertThat(displayId).isEqualTo(3) + } + + @Test + fun displayId_onlyDefaultDisplay_defaultDisplay() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays(display(id = 0, type = Display.TYPE_INTERNAL)) + + assertThat(displayId).isEqualTo(0) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt index 016a24acd461..982c51b8318c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.shade.domain.interactor import android.content.Context -import android.content.MutableContextWrapper import android.content.res.Configuration import android.content.res.Resources import android.view.Display @@ -30,7 +29,6 @@ import com.android.systemui.display.data.repository.FakeDisplayWindowPropertiesR import com.android.systemui.display.shared.model.DisplayWindowProperties import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.data.repository.FakeShadeDisplayRepository -import com.android.systemui.statusbar.phone.ConfigurationForwarder import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -39,6 +37,7 @@ import kotlinx.coroutines.test.advanceUntilIdle import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.inOrder import org.mockito.Mockito.verify import org.mockito.kotlin.any import org.mockito.kotlin.eq @@ -53,13 +52,10 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { private val shadeRootview = mock<WindowRootView>() private val positionRepository = FakeShadeDisplayRepository() - private val defaultContext = mock<Context>() - private val secondaryContext = mock<Context>() + private val shadeContext = mock<Context>() private val contextStore = FakeDisplayWindowPropertiesRepository() private val testScope = TestScope(UnconfinedTestDispatcher()) - private val configurationForwarder = mock<ConfigurationForwarder>() - private val defaultWm = mock<WindowManager>() - private val secondaryWm = mock<WindowManager>() + private val shadeWm = mock<WindowManager>() private val resources = mock<Resources>() private val configuration = mock<Configuration>() private val display = mock<Display>() @@ -68,11 +64,9 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { ShadeDisplaysInteractor( Optional.of(shadeRootview), positionRepository, - MutableContextWrapper(defaultContext), - resources, - contextStore, + shadeContext, + shadeWm, testScope.backgroundScope, - configurationForwarder, testScope.backgroundScope.coroutineContext, ) @@ -83,28 +77,15 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(resources.configuration).thenReturn(configuration) - whenever(defaultContext.displayId).thenReturn(0) - whenever(defaultContext.getSystemService(any())).thenReturn(defaultWm) - whenever(defaultContext.resources).thenReturn(resources) + whenever(shadeContext.displayId).thenReturn(0) + whenever(shadeContext.getSystemService(any())).thenReturn(shadeWm) + whenever(shadeContext.resources).thenReturn(resources) contextStore.insert( DisplayWindowProperties( displayId = 0, windowType = TYPE_NOTIFICATION_SHADE, - context = defaultContext, - windowManager = defaultWm, - layoutInflater = mock(), - ) - ) - - whenever(secondaryContext.displayId).thenReturn(1) - whenever(secondaryContext.getSystemService(any())).thenReturn(secondaryWm) - whenever(secondaryContext.resources).thenReturn(resources) - contextStore.insert( - DisplayWindowProperties( - displayId = 1, - windowType = TYPE_NOTIFICATION_SHADE, - context = secondaryContext, - windowManager = secondaryWm, + context = shadeContext, + windowManager = shadeWm, layoutInflater = mock(), ) ) @@ -117,8 +98,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { interactor.start() testScope.advanceUntilIdle() - verifyNoMoreInteractions(defaultWm) - verifyNoMoreInteractions(secondaryWm) + verifyNoMoreInteractions(shadeWm) } @Test @@ -127,8 +107,10 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { positionRepository.setDisplayId(1) interactor.start() - verify(defaultWm).removeView(eq(shadeRootview)) - verify(secondaryWm).addView(eq(shadeRootview), any()) + inOrder(shadeWm).apply { + verify(shadeWm).removeView(eq(shadeRootview)) + verify(shadeWm).addView(eq(shadeRootview), any()) + } } @Test @@ -139,18 +121,9 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { positionRepository.setDisplayId(1) - verify(defaultWm).removeView(eq(shadeRootview)) - verify(secondaryWm).addView(eq(shadeRootview), any()) - } - - @Test - fun start_shadePositionChanges_newConfigPropagated() { - whenever(display.displayId).thenReturn(0) - positionRepository.setDisplayId(0) - interactor.start() - - positionRepository.setDisplayId(1) - - verify(configurationForwarder).onConfigurationChanged(eq(configuration)) + inOrder(shadeWm).apply { + verify(shadeWm).removeView(eq(shadeRootview)) + verify(shadeWm).addView(eq(shadeRootview), any()) + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java index 3ad0605f2781..3ad0605f2781 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index da0029ff6746..32a9f544ae17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -48,7 +48,6 @@ 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.anyObject; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -1633,7 +1632,7 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll } else { verify(mRotateTextViewController).hideIndication(type); verify(mRotateTextViewController, never()).updateIndication(eq(type), - anyObject(), anyBoolean()); + any(), anyBoolean()); } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt index 9907740672ed..cd66ef32180a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt @@ -26,10 +26,10 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.mockito.mock import kotlinx.coroutines.flow.MutableStateFlow import org.junit.Before @@ -76,7 +76,7 @@ class PulseExpansionHandlerTest : SysuiTestCase() { falsingManager, shadeInteractor, lockscreenShadeTransitionController, - dumpManager + dumpManager, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt new file mode 100644 index 000000000000..7fed47a4653e --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.activity.data.repository.activityManagerRepository +import com.android.systemui.activity.data.repository.fake +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +class SingleNotificationChipInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + val factory = kosmos.singleNotificationChipInteractorFactory + + @Test + fun notificationChip_startsWithStartingModel() = + kosmos.runTest { + val icon = mock<StatusBarIconView>() + val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = icon) + + val underTest = factory.create(startingNotif) + + val latest by collectLastValue(underTest.notificationChip) + + assertThat(latest!!.key).isEqualTo("notif1") + assertThat(latest!!.statusBarChipIconView).isEqualTo(icon) + } + + @Test + fun notificationChip_updatesAfterSet() = + kosmos.runTest { + val originalIconView = mock<StatusBarIconView>() + val underTest = + factory.create( + activeNotificationModel(key = "notif1", statusBarChipIcon = originalIconView) + ) + + val latest by collectLastValue(underTest.notificationChip) + + val newIconView = mock<StatusBarIconView>() + underTest.setNotification( + activeNotificationModel(key = "notif1", statusBarChipIcon = newIconView) + ) + + assertThat(latest!!.key).isEqualTo("notif1") + assertThat(latest!!.statusBarChipIconView).isEqualTo(newIconView) + } + + @Test + fun notificationChip_ignoresSetWithDifferentKey() = + kosmos.runTest { + val originalIconView = mock<StatusBarIconView>() + val underTest = + factory.create( + activeNotificationModel(key = "notif1", statusBarChipIcon = originalIconView) + ) + + val latest by collectLastValue(underTest.notificationChip) + + val newIconView = mock<StatusBarIconView>() + underTest.setNotification( + activeNotificationModel(key = "other_notif", statusBarChipIcon = newIconView) + ) + + assertThat(latest!!.key).isEqualTo("notif1") + assertThat(latest!!.statusBarChipIconView).isEqualTo(originalIconView) + } + + @Test + fun notificationChip_missingStatusBarIconChipView_inConstructor_emitsNull() = + kosmos.runTest { + val underTest = + factory.create(activeNotificationModel(key = "notif1", statusBarChipIcon = null)) + + val latest by collectLastValue(underTest.notificationChip) + + assertThat(latest).isNull() + } + + @Test + fun notificationChip_missingStatusBarIconChipView_inSet_emitsNull() = + kosmos.runTest { + val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = mock()) + val underTest = factory.create(startingNotif) + val latest by collectLastValue(underTest.notificationChip) + assertThat(latest).isNotNull() + + underTest.setNotification( + activeNotificationModel(key = "notif1", statusBarChipIcon = null) + ) + + assertThat(latest).isNull() + } + + @Test + fun notificationChip_appIsVisibleOnCreation_emitsNull() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = true + + val underTest = + factory.create( + activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock()) + ) + + val latest by collectLastValue(underTest.notificationChip) + + assertThat(latest).isNull() + } + + @Test + fun notificationChip_appNotVisibleOnCreation_emitsValue() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = false + + val underTest = + factory.create( + activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock()) + ) + + val latest by collectLastValue(underTest.notificationChip) + + assertThat(latest).isNotNull() + } + + @Test + fun notificationChip_hidesWhenAppIsVisible() = + kosmos.runTest { + val underTest = + factory.create( + activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock()) + ) + + val latest by collectLastValue(underTest.notificationChip) + + activityManagerRepository.fake.setIsAppVisible(UID, false) + assertThat(latest).isNotNull() + + activityManagerRepository.fake.setIsAppVisible(UID, true) + assertThat(latest).isNull() + + activityManagerRepository.fake.setIsAppVisible(UID, false) + assertThat(latest).isNotNull() + } + + // Note: This test is theoretically impossible because the notification key should contain the + // UID, so if the UID changes then the key would also change and a new interactor would be + // created. But, test it just in case. + @Test + fun notificationChip_updatedUid_rechecksAppVisibility_oldObserverUnregistered() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = false + + val hiddenUid = 100 + val shownUid = 101 + + val underTest = + factory.create( + activeNotificationModel( + key = "notif", + uid = hiddenUid, + statusBarChipIcon = mock(), + ) + ) + val latest by collectLastValue(underTest.notificationChip) + assertThat(latest).isNotNull() + + // WHEN the notif gets a new UID that starts as visible + activityManagerRepository.fake.startingIsAppVisibleValue = true + underTest.setNotification( + activeNotificationModel(key = "notif", uid = shownUid, statusBarChipIcon = mock()) + ) + + // THEN we re-fetch the app visibility state with the new UID, and since that UID is + // visible, we hide the chip + assertThat(latest).isNull() + } + + companion object { + private const val UID = 885 + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt index 19ed6a57d2f0..702e101d2d39 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt @@ -16,30 +16,277 @@ package com.android.systemui.statusbar.chips.notification.domain.interactor +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest import org.junit.runner.RunWith +import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) -@EnableFlags(StatusBarNotifChips.FLAG_NAME) class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope + private val activeNotificationListRepository = kosmos.activeNotificationListRepository - private val underTest = kosmos.statusBarNotificationChipsInteractor + private val underTest by lazy { + kosmos.statusBarNotificationChipsInteractor.also { it.start() } + } @Test + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_flagOff_noNotifs() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock<StatusBarIconView>(), + isPromoted = true, + ) + ) + ) + + assertThat(latest).isEmpty() + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_noNotifs_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + setNotifs(emptyList()) + + assertThat(latest).isEmpty() + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_notifMissingStatusBarChipIconView_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = null, + isPromoted = true, + ) + ) + ) + + assertThat(latest).isEmpty() + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_onePromotedNotif_statusBarIconViewMatches() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + val icon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = icon, + isPromoted = true, + ) + ) + ) + + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif") + assertThat(latest!![0].statusBarChipIconView).isEqualTo(icon) + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_onlyForPromotedNotifs() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel( + key = "notif1", + statusBarChipIcon = firstIcon, + isPromoted = true, + ), + activeNotificationModel( + key = "notif2", + statusBarChipIcon = secondIcon, + isPromoted = true, + ), + activeNotificationModel( + key = "notif3", + statusBarChipIcon = mock<StatusBarIconView>(), + isPromoted = false, + ), + ) + ) + + assertThat(latest).hasSize(2) + assertThat(latest!![0].key).isEqualTo("notif1") + assertThat(latest!![0].statusBarChipIconView).isEqualTo(firstIcon) + assertThat(latest!![1].key).isEqualTo("notif2") + assertThat(latest!![1].statusBarChipIconView).isEqualTo(secondIcon) + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_notifUpdatesGoThrough() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + val thirdIcon = mock<StatusBarIconView>() + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = firstIcon, + isPromoted = true, + ) + ) + ) + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif") + assertThat(latest!![0].statusBarChipIconView).isEqualTo(firstIcon) + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = secondIcon, + isPromoted = true, + ) + ) + ) + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif") + assertThat(latest!![0].statusBarChipIconView).isEqualTo(secondIcon) + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = thirdIcon, + isPromoted = true, + ) + ) + ) + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif") + assertThat(latest!![0].statusBarChipIconView).isEqualTo(thirdIcon) + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_promotedNotifDisappearsThenReappears() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock(), + isPromoted = true, + ) + ) + ) + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif") + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock(), + isPromoted = false, + ) + ) + ) + assertThat(latest).isEmpty() + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock(), + isPromoted = true, + ) + ) + ) + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif") + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun notificationChips_notifChangesKey() = + testScope.runTest { + val latest by collectLastValue(underTest.notificationChips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel( + key = "notif|uid1", + statusBarChipIcon = firstIcon, + isPromoted = true, + ) + ) + ) + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif|uid1") + assertThat(latest!![0].statusBarChipIconView).isEqualTo(firstIcon) + + // WHEN a notification changes UID, which is a key change + setNotifs( + listOf( + activeNotificationModel( + key = "notif|uid2", + statusBarChipIcon = secondIcon, + isPromoted = true, + ) + ) + ) + + // THEN we correctly update + assertThat(latest).hasSize(1) + assertThat(latest!![0].key).isEqualTo("notif|uid2") + assertThat(latest!![0].statusBarChipIconView).isEqualTo(secondIcon) + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun onPromotedNotificationChipTapped_emitsKeys() = testScope.runTest { val latest by collectValues(underTest.promotedNotificationChipTapEvent) @@ -56,6 +303,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun onPromotedNotificationChipTapped_sameKeyTwice_emitsTwice() = testScope.runTest { val latest by collectValues(underTest.promotedNotificationChipTapEvent) @@ -67,4 +315,11 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { assertThat(latest[0]).isEqualTo("fakeKey") assertThat(latest[1]).isEqualTo("fakeKey") } + + private fun setNotifs(notifs: List<ActiveNotificationModel>) { + activeNotificationListRepository.activeNotifications.value = + ActiveNotificationsStore.Builder() + .apply { notifs.forEach { addIndividualNotif(it) } } + .build() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt index 1b4132910555..16376c5b3850 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -22,7 +22,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips @@ -34,26 +36,28 @@ import com.android.systemui.statusbar.notification.shared.ActiveNotificationMode import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.test.Test -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.runner.RunWith import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) -@OptIn(ExperimentalCoroutinesApi::class) @EnableFlags(StatusBarNotifChips.FLAG_NAME) class NotifChipsViewModelTest : SysuiTestCase() { - private val kosmos = testKosmos() - private val testScope = kosmos.testScope + private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val activeNotificationListRepository = kosmos.activeNotificationListRepository - private val underTest = kosmos.notifChipsViewModel + private val underTest by lazy { kosmos.notifChipsViewModel } + + @Before + fun setUp() { + kosmos.statusBarNotificationChipsInteractor.start() + } @Test fun chips_noNotifs_empty() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.chips) setNotifs(emptyList()) @@ -63,7 +67,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test fun chips_notifMissingStatusBarChipIconView_empty() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.chips) setNotifs( @@ -81,7 +85,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test fun chips_onePromotedNotif_statusBarIconViewMatches() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.chips) val icon = mock<StatusBarIconView>() @@ -103,7 +107,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test fun chips_onlyForPromotedNotifs() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.chips) val firstIcon = mock<StatusBarIconView>() @@ -135,7 +139,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test fun chips_clickingChipNotifiesInteractor() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.chips) val latestChipTap by collectLastValue( @@ -163,7 +167,6 @@ class NotifChipsViewModelTest : SysuiTestCase() { ActiveNotificationsStore.Builder() .apply { notifs.forEach { addIndividualNotif(it) } } .build() - testScope.runCurrent() } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt index 25d5ce50e03f..eb0978eff24b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt @@ -27,6 +27,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask @@ -38,6 +39,7 @@ import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.Me import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel @@ -67,6 +69,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any @@ -79,7 +82,7 @@ import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @EnableFlags(StatusBarNotifChips.FLAG_NAME) class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { - private val kosmos = testKosmos() + private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock private val commandRegistry = kosmos.commandRegistry @@ -103,12 +106,13 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { .thenReturn(chipBackgroundView) } - private val underTest = kosmos.ongoingActivityChipsViewModel + private val underTest by lazy { kosmos.ongoingActivityChipsViewModel } @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) kosmos.demoNotifChipViewModel.start() + kosmos.statusBarNotificationChipsInteractor.start() val icon = BitmapDrawable( context.resources, @@ -616,6 +620,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { } @Test + @Ignore("b/364653005") // We'll need to re-do the animation story when we implement RON chips fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt index cb92b7745961..a1772e3f62ed 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt @@ -14,11 +14,11 @@ import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.HeadsUpUtil import com.android.systemui.testKosmos import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -69,7 +69,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { headsUpManager, notification, kosmos.interactionJankMonitor, - onFinishAnimationCallback + onFinishAnimationCallback, ) } @@ -95,7 +95,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { notificationKey, /* releaseImmediately= */ true, /* animate= */ true, - /* reason= */ "onIntentStarted(willAnimate=false)" + /* reason= */ "onIntentStarted(willAnimate=false)", ) verify(onFinishAnimationCallback).run() } @@ -118,7 +118,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { notificationKey, /* releaseImmediately= */ true, /* animate= */ true, - /* reason= */ "onLaunchAnimationCancelled()" + /* reason= */ "onLaunchAnimationCancelled()", ) verify(onFinishAnimationCallback).run() } @@ -141,7 +141,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { notificationKey, /* releaseImmediately= */ true, /* animate= */ false, - /* reason= */ "onLaunchAnimationEnd()" + /* reason= */ "onLaunchAnimationEnd()", ) verify(onFinishAnimationCallback).run() } @@ -180,14 +180,14 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { summary.key, /* releaseImmediately= */ true, /* animate= */ false, - /* reason= */ "onLaunchAnimationEnd()" + /* reason= */ "onLaunchAnimationEnd()", ) verify(headsUpManager, never()) .removeNotification( notification.entry.key, /* releaseImmediately= */ true, /* animate= */ false, - /* reason= */ "onLaunchAnimationEnd()" + /* reason= */ "onLaunchAnimationEnd()", ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt index c4b1b841c6a5..0dc01a65df19 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -39,13 +39,13 @@ import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_WAKEUP import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 2c488e3a7242..1d7f25784327 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -38,12 +38,13 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.mockNotifCollection -import com.android.systemui.statusbar.notification.collection.notifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.NodeController +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.DecisionImpl @@ -51,8 +52,6 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback import com.android.systemui.statusbar.phone.NotificationGroupTestHelper -import com.android.systemui.statusbar.policy.BaseHeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -84,7 +83,9 @@ import org.mockito.MockitoAnnotations class HeadsUpCoordinatorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope - private val statusBarNotificationChipsInteractor = kosmos.statusBarNotificationChipsInteractor + private val statusBarNotificationChipsInteractor by lazy { + kosmos.statusBarNotificationChipsInteractor + } private val notifCollection = kosmos.mockNotifCollection private lateinit var coordinator: HeadsUpCoordinator @@ -101,7 +102,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private val notifPipeline: NotifPipeline = mock() private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true) - private val headsUpManager: BaseHeadsUpManager = mock() + private val headsUpManager: HeadsUpManagerImpl = mock() private val headsUpViewBinder: HeadsUpViewBinder = mock() private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock() private val remoteInputManager: NotificationRemoteInputManager = mock() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt index d772e3effbeb..14148cdc0f03 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt @@ -21,7 +21,6 @@ import android.app.Notification import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_LOW import android.platform.test.annotations.EnableFlags -import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -43,6 +42,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.domain.interactor.lockScreenNotificationMinimalismSetting +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.shared.NotificationMinimalism import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.testKosmos @@ -394,7 +394,7 @@ class LockScreenMinimalismCoordinatorTest : SysuiTestCase() { assertThatTopUnseenKey().isEqualTo(solo1.key) // TEST: even being pinned doesn't take effect immediately - hunRepo1.isPinned.value = true + hunRepo1.pinnedStatus.value = PinnedStatus.PinnedBySystem testScheduler.advanceTimeBy(0.5.seconds) onBeforeTransformGroupsListener.onBeforeTransformGroups(listEntryList) assertThatTopUnseenKey().isEqualTo(solo1.key) @@ -406,8 +406,8 @@ class LockScreenMinimalismCoordinatorTest : SysuiTestCase() { // TEST: repeat; being heads up and pinned for 1 second triggers seen kosmos.headsUpNotificationRepository.orderedHeadsUpRows.value = listOf(hunRepo2) - hunRepo1.isPinned.value = false - hunRepo2.isPinned.value = true + hunRepo1.pinnedStatus.value = PinnedStatus.NotPinned + hunRepo2.pinnedStatus.value = PinnedStatus.PinnedBySystem testScheduler.advanceTimeBy(1.seconds) onBeforeTransformGroupsListener.onBeforeTransformGroups(listEntryList) assertThatTopUnseenKey().isEqualTo(null) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt index 3fd9c2160ce2..d38fb5050c5f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt @@ -27,7 +27,6 @@ import com.android.systemui.flags.andSceneContainer import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardRepository -import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -48,9 +47,9 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.lockScreenShowOnlyUnseenNotificationsSetting import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.fakeSettings @@ -155,7 +154,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu runKeyguardCoordinatorTest { kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE), ) // WHEN: A notification is posted @@ -170,7 +169,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(true) kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD), ) // THEN: The notification is recognized as "seen" and is filtered out. @@ -180,7 +179,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(false) kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.AOD, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.AOD, KeyguardState.GONE), ) // THEN: The notification is shown regardless @@ -359,14 +358,14 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(false) kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE), ) // WHEN: Keyguard is shown again keyguardRepository.setKeyguardShowing(true) kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD), ) // THEN: The notification is now recognized as "seen" and is filtered out. @@ -412,7 +411,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu runKeyguardCoordinatorTest { kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN), ) val firstEntry = NotificationEntryBuilder().setId(1).build() collectionListener.onEntryAdded(firstEntry) @@ -435,14 +434,14 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(false) kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE), ) // WHEN: Keyguard is shown again keyguardRepository.setKeyguardShowing(true) kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN), ) // THEN: The first notification is considered seen and is filtered out. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java index 3ad41a54ac7e..ba85e32484df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -67,7 +67,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt index d717fe4c1e04..dc0231f40609 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.shade.shadeTestUtil import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.testKosmos @@ -102,7 +103,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { } @Test - fun hasPinnedRows_rowGetsPinned_true() = + fun hasPinnedRows_rowGetsPinnedNormally_true() = testScope.runTest { val hasPinnedRows by collectLastValue(underTest.hasPinnedRows) // GIVEN no rows are pinned @@ -115,8 +116,30 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { headsUpRepository.setNotifications(rows) runCurrent() - // WHEN a row gets pinned - rows[0].isPinned.value = true + // WHEN a row gets pinned normally + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem + runCurrent() + + // THEN hasPinnedRows updates to true + assertThat(hasPinnedRows).isTrue() + } + + @Test + fun hasPinnedRows_rowGetsPinnedByUser_true() = + testScope.runTest { + val hasPinnedRows by collectLastValue(underTest.hasPinnedRows) + // GIVEN no rows are pinned + val rows = + arrayListOf( + fakeHeadsUpRowRepository("key 0"), + fakeHeadsUpRowRepository("key 1"), + fakeHeadsUpRowRepository("key 2"), + ) + headsUpRepository.setNotifications(rows) + runCurrent() + + // WHEN a row gets pinned due to a chip tap + rows[0].pinnedStatus.value = PinnedStatus.PinnedByUser runCurrent() // THEN hasPinnedRows updates to true @@ -138,7 +161,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // THEN that row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN hasPinnedRows updates to false @@ -246,7 +269,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // WHEN all rows gets pinned - rows[2].isPinned.value = true + rows[2].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN no rows are filtered @@ -271,7 +294,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { assertThat(activeHeadsUpRows).containsExactly(rows[0], rows[1], rows[2]) // WHEN all rows gets pinned - rows[2].isPinned.value = true + rows[2].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN no change @@ -329,7 +352,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // WHEN a row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN the unpinned row is filtered @@ -351,7 +374,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // WHEN a row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN all rows are still present @@ -372,15 +395,15 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { headsUpRepository.setNotifications(rows) runCurrent() - rows[0].isPinned.value = true + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() assertThat(pinnedHeadsUpRows).containsExactly(rows[0]) - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() assertThat(pinnedHeadsUpRows).isEmpty() - rows[0].isPinned.value = true + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() assertThat(pinnedHeadsUpRows).containsExactly(rows[0]) } @@ -485,7 +508,5 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { } private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean = false) = - FakeHeadsUpRowRepository(key = key, elementKey = Any()).apply { - this.isPinned.value = isPinned - } + FakeHeadsUpRowRepository(key = key, isPinned = isPinned) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt index c5eed7365d92..22a9c64d2cc9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import android.app.Notification import android.os.Handler @@ -31,9 +31,11 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerTestUtil.createFullScreenIntentEntry import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.phone.keyguardBypassController -import com.android.systemui.statusbar.policy.HeadsUpManagerTestUtil.createFullScreenIntentEntry +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.kotlin.JavaAdapter @@ -76,7 +78,7 @@ class AvalancheControllerTest : SysuiTestCase() { private val mGlobalSettings = FakeGlobalSettings() private val mSystemClock = FakeSystemClock() private val mExecutor = FakeExecutor(mSystemClock) - private lateinit var testableHeadsUpManager: BaseHeadsUpManager + private lateinit var testableHeadsUpManager: HeadsUpManagerImpl @Before fun setUp() { @@ -114,7 +116,7 @@ class AvalancheControllerTest : SysuiTestCase() { ) } - private fun createHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry { + private fun createHeadsUpEntry(id: Int): HeadsUpManagerImpl.HeadsUpEntry { return testableHeadsUpManager.createHeadsUpEntry( NotificationEntryBuilder() .setSbn(HeadsUpManagerTestUtil.createSbn(id, Notification.Builder(mContext, ""))) @@ -122,7 +124,7 @@ class AvalancheControllerTest : SysuiTestCase() { ) } - private fun createFsiHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry { + private fun createFsiHeadsUpEntry(id: Int): HeadsUpManagerImpl.HeadsUpEntry { return testableHeadsUpManager.createHeadsUpEntry(createFullScreenIntentEntry(id, mContext)) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java index 0fbee6d29441..01f78cb289fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED; @@ -54,18 +54,17 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.res.R; import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.FakeGlobalSettings; import com.android.systemui.util.time.FakeSystemClock; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,6 +72,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import kotlinx.coroutines.flow.StateFlowKt; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -81,8 +81,8 @@ import java.util.List; @SmallTest @TestableLooper.RunWithLooper @RunWith(ParameterizedAndroidJunit4.class) -// TODO(b/378142453): Merge this with BaseHeadsUpManagerTest. -public class BaseHeadsUpManagerTest extends SysuiTestCase { +// TODO(b/378142453): Merge this with HeadsUpManagerPhoneTest. +public class HeadsUpManagerImplTest extends SysuiTestCase { protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); @Rule @@ -119,7 +119,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME); } - private BaseHeadsUpManager createHeadsUpManager() { + private HeadsUpManagerImpl createHeadsUpManager() { return new TestableHeadsUpManager( mContext, mLogger, @@ -169,7 +169,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { return FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME); } - public BaseHeadsUpManagerTest(FlagsParameterization flags) { + public HeadsUpManagerImplTest(FlagsParameterization flags) { mSetFlagsRule.setFlagsParameterization(flags); } @@ -184,7 +184,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testHasNotifications_headsUpManagerMapNotEmpty_true() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); bhum.showNotification(entry); @@ -195,10 +195,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testHasNotifications_avalancheMapNotEmpty_true() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); mAvalancheController.addToNext(headsUpEntry, () -> {}); assertThat(mAvalancheController.getWaitingEntryList()).isNotEmpty(); @@ -208,7 +208,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testHasNotifications_false() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); assertThat(bhum.mHeadsUpEntryMap).isEmpty(); assertThat(mAvalancheController.getWaitingEntryList()).isEmpty(); assertThat(bhum.hasNotifications()).isFalse(); @@ -217,10 +217,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testGetHeadsUpEntryList_includesAvalancheEntryList() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); mAvalancheController.addToNext(headsUpEntry, () -> {}); assertThat(bhum.getHeadsUpEntryList()).contains(headsUpEntry); @@ -229,10 +229,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testGetHeadsUpEntry_returnsAvalancheEntry() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); mAvalancheController.addToNext(headsUpEntry, () -> {}); assertThat(bhum.getHeadsUpEntry(notifEntry.getKey())).isEqualTo(headsUpEntry); @@ -240,7 +240,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_addsEntry() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -252,7 +252,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismisses() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -263,7 +263,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_removeDeferred() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -276,7 +276,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_forceRemove() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -289,7 +289,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testReleaseAllImmediately() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) { final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(i, mContext); entry.setRow(mRow); @@ -303,7 +303,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testCanRemoveImmediately_notShownLongEnough() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -314,11 +314,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testHunRemovedLogging() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = mock( - BaseHeadsUpManager.HeadsUpEntry.class); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = mock( + HeadsUpManagerImpl.HeadsUpEntry.class); + when(headsUpEntry.getPinnedStatus()) + .thenReturn(StateFlowKt.MutableStateFlow(PinnedStatus.NotPinned)); headsUpEntry.mEntry = notifEntry; hum.onEntryRemoved(headsUpEntry, "test"); @@ -329,7 +331,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -342,7 +344,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismissesWithDefaultTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -356,7 +358,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -370,7 +372,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_sticky_neverAutoDismisses() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -383,7 +385,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismissesWithAccessibilityTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(true); @@ -397,7 +399,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(true); @@ -411,7 +413,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_beforeMinimumDisplayTime() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -430,7 +432,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_afterMinimumDisplayTime() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -448,7 +450,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_releaseImmediately() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); hum.showNotification(entry); @@ -462,7 +464,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_rowPinnedAndExpanded_true() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); when(mRow.isPinned()).thenReturn(true); @@ -470,7 +472,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { hum.showNotification(notifEntry); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( notifEntry.getKey()); headsUpEntry.setExpanded(true); @@ -479,13 +481,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_remoteInputActive_true() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); hum.showNotification(notifEntry); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( notifEntry.getKey()); headsUpEntry.mRemoteInputActive = true; @@ -494,7 +496,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_hasFullScreenIntent_true() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext); @@ -506,7 +508,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_stickyForSomeTime_false() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); hum.showNotification(entry); @@ -517,13 +519,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_false() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); hum.showNotification(notifEntry); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( notifEntry.getKey()); headsUpEntry.setExpanded(false); headsUpEntry.mRemoteInputActive = false; @@ -533,7 +535,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testCompareTo_withNullEntries() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); hum.showNotification(alertEntry); @@ -545,7 +547,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testCompareTo_withNonAlertEntries() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag( "nae1").build(); @@ -561,9 +563,9 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); - final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry( new NotificationEntryBuilder() .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, new Notification.Builder(mContext, "") @@ -571,7 +573,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { .setOngoing(true))) .build()); - final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); activeRemoteInput.mRemoteInputActive = true; @@ -581,11 +583,11 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final Person person = new Person.Builder().setName("person").build(); final PendingIntent intent = mock(PendingIntent.class); - final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry incomingCall = hum.new HeadsUpEntry( new NotificationEntryBuilder() .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, new Notification.Builder(mContext, "") @@ -593,7 +595,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { .forIncomingCall(person, intent, intent)))) .build()); - final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); activeRemoteInput.mRemoteInputActive = true; @@ -604,10 +606,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testPinEntry_logsPeek_throttleEnabled() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); // Needs full screen intent in order to be pinned - final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext)); // Note: the standard way to show a notification would be calling showNotification rather @@ -620,17 +622,17 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { assertEquals(2, mUiEventLoggerFake.numLogs()); assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(), mUiEventLoggerFake.eventId(0)); - assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), + assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(1)); } @Test @DisableFlags(NotificationThrottleHun.FLAG_NAME) public void testPinEntry_logsPeek_throttleDisabled() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); // Needs full screen intent in order to be pinned - final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext)); // Note: the standard way to show a notification would be calling showNotification rather @@ -641,13 +643,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { hum.onEntryAdded(entryToPin); assertEquals(1, mUiEventLoggerFake.numLogs()); - assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), + assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(0)); } @Test public void testSetUserActionMayIndirectlyRemove() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt index 6175e05923a7..35d825310fdc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import android.os.Handler import android.platform.test.annotations.EnableFlags @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.mockExecutorHandler import com.android.systemui.util.kotlin.JavaAdapter @@ -58,7 +59,7 @@ import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(ParameterizedAndroidJunit4::class) @RunWithLooper -class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManagerTest(flags) { +class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : HeadsUpManagerImplTest(flags) { private val mHeadsUpManagerLogger = HeadsUpManagerLogger(logcatLogBuffer()) @@ -87,8 +88,8 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager @Mock private lateinit var mBgHandler: Handler - private fun createHeadsUpManagerPhone(): BaseHeadsUpManager { - return BaseHeadsUpManager( + private fun createHeadsUpManagerPhone(): HeadsUpManagerImpl { + return HeadsUpManagerImpl( mContext, mHeadsUpManagerLogger, statusBarStateController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerTestUtil.java index 306d6efd40b5..684ce59d6129 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerTestUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java index 59987f413ad6..2b077ed7f80d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler; @@ -33,12 +33,14 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.time.SystemClock; -class TestableHeadsUpManager extends BaseHeadsUpManager { +class TestableHeadsUpManager extends HeadsUpManagerImpl { private HeadsUpEntry mLastCreatedEntry; @@ -76,10 +78,10 @@ class TestableHeadsUpManager extends BaseHeadsUpManager { shadeInteractor, avalancheController); - mTouchAcceptanceDelay = BaseHeadsUpManagerTest.TEST_TOUCH_ACCEPTANCE_TIME; - mMinimumDisplayTime = BaseHeadsUpManagerTest.TEST_MINIMUM_DISPLAY_TIME; - mAutoDismissTime = BaseHeadsUpManagerTest.TEST_AUTO_DISMISS_TIME; - mStickyForSomeTimeAutoDismissTime = BaseHeadsUpManagerTest.TEST_STICKY_AUTO_DISMISS_TIME; + mTouchAcceptanceDelay = HeadsUpManagerImplTest.TEST_TOUCH_ACCEPTANCE_TIME; + mMinimumDisplayTime = HeadsUpManagerImplTest.TEST_MINIMUM_DISPLAY_TIME; + mAutoDismissTime = HeadsUpManagerImplTest.TEST_AUTO_DISMISS_TIME; + mStickyForSomeTimeAutoDismissTime = HeadsUpManagerImplTest.TEST_STICKY_AUTO_DISMISS_TIME; } @NonNull diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt index 284efc71a96d..d3bde84ad0c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt @@ -70,13 +70,13 @@ import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.FakeEventLog import com.android.systemui.util.settings.FakeGlobalSettings import com.android.systemui.util.settings.FakeSettings @@ -109,7 +109,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true }, - systrace = false + systrace = false, ) private val leakCheck = LeakCheckedTest.SysuiLeakCheck() @@ -162,10 +162,10 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { @Before fun setUp() { val userId = ActivityManager.getCurrentUser() - val user = UserInfo(userId, "Current user", /* flags = */ 0) + val user = UserInfo(userId, "Current user", /* flags= */ 0) deviceProvisionedController.currentUser = userId - userTracker.set(listOf(user), /* currentUserIndex = */ 0) + userTracker.set(listOf(user), /* currentUserIndex= */ 0) systemSettings = FakeSettings() whenever(bubbles.canShowBubbleNotification()).thenReturn(true) whenever(settingsInteractor.isCooldownEnabled).thenReturn(MutableStateFlow(true)) @@ -491,7 +491,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { private fun withPeekAndPulseEntry( extendEntry: EntryBuilder.() -> Unit, - block: (NotificationEntry) -> Unit + block: (NotificationEntry) -> Unit, ) { ensurePeekState() block(buildPeekEntry(extendEntry)) @@ -540,10 +540,10 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG) fun testShouldNotHeadsUp_silentNotification() { withPeekAndPulseEntry({ - isGrouped = false - isGroupSummary = false - isSilent = true - }) { + isGrouped = false + isGroupSummary = false + isSilent = true + }) { assertShouldNotHeadsUp(it) assertNoEventsLogged() } @@ -553,10 +553,10 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG) fun testShouldHeadsUp_silentNotificationFalse() { withPeekAndPulseEntry({ - isGrouped = false - isGroupSummary = false - isSilent = false - }) { + isGrouped = false + isGroupSummary = false + isSilent = false + }) { assertShouldHeadsUp(it) assertNoEventsLogged() } @@ -697,7 +697,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { forEachFsiState { assertShouldNotFsi( buildFsiEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT }, - expectWouldInterruptWithoutDnd = true + expectWouldInterruptWithoutDnd = true, ) assertNoEventsLogged() } @@ -719,7 +719,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT importance = IMPORTANCE_DEFAULT }, - expectWouldInterruptWithoutDnd = false + expectWouldInterruptWithoutDnd = false, ) assertNoEventsLogged() } @@ -754,7 +754,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { assertUiEventLogged( FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, entry.sbn.uid, - entry.sbn.packageName + entry.sbn.packageName, ) assertSystemEventLogged("231322873", entry.sbn.uid, "groupAlertBehavior") } @@ -813,7 +813,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { assertUiEventLogged( FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA, entry.sbn.uid, - entry.sbn.packageName + entry.sbn.packageName, ) assertSystemEventLogged("274759612", entry.sbn.uid, "bubbleMetadata") } @@ -960,7 +960,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { var keyguardIsShowing: Boolean = false, var keyguardIsOccluded: Boolean = false, var deviceProvisioned: Boolean = true, - var currentUserSetup: Boolean = true + var currentUserSetup: Boolean = true, ) protected fun setState(state: State): Unit = @@ -1131,7 +1131,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected fun withLegacySuppressor( suppressor: NotificationInterruptSuppressor, - block: () -> Unit + block: () -> Unit, ) { provider.addLegacySuppressor(suppressor) block() @@ -1166,7 +1166,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected fun assertShouldNotFsi( entry: NotificationEntry, - expectWouldInterruptWithoutDnd: Boolean? = null + expectWouldInterruptWithoutDnd: Boolean? = null, ) = provider.makeUnloggedFullScreenIntentDecision(entry).let { provider.logFullScreenIntentDecision(it) @@ -1175,7 +1175,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { assertEquals( "unexpected wouldInterruptWithoutDnd for FSI: ${it.logReason}", expectWouldInterruptWithoutDnd, - it.wouldInterruptWithoutDnd + it.wouldInterruptWithoutDnd, ) } } @@ -1227,9 +1227,9 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { context, /* requestCode = */ 0, Intent().setPackage(context.packageName), - FLAG_MUTABLE + FLAG_MUTABLE, ), - Icon.createWithResource(context.resources, R.drawable.android) + Icon.createWithResource(context.resources, R.drawable.android), ) } @@ -1272,7 +1272,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { } if (hasFsi) { - nb.setFullScreenIntent(mock(), /* highPriority = */ true) + nb.setFullScreenIntent(mock(), /* highPriority= */ true) } } .build() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt index 7fd9c9fbdd6b..ff8ef189eb76 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt @@ -27,9 +27,9 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.settings.UserTracker import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor import com.android.systemui.statusbar.notification.NotifPipelineFlags +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings @@ -63,7 +63,7 @@ object VisualInterruptionDecisionProviderTestUtil { bubbles: Optional<Bubbles>, context: Context, notificationManager: NotificationManager, - settingsInteractor: NotificationSettingsInteractor + settingsInteractor: NotificationSettingsInteractor, ): VisualInterruptionDecisionProvider { return if (VisualInterruptionRefactor.isEnabled) { VisualInterruptionDecisionProviderImpl( @@ -88,7 +88,7 @@ object VisualInterruptionDecisionProviderTestUtil { bubbles, context, notificationManager, - settingsInteractor + settingsInteractor, ) } else { NotificationInterruptStateProviderWrapper( @@ -109,7 +109,7 @@ object VisualInterruptionDecisionProviderTestUtil { systemClock, globalSettings, eventLog, - bubbles + bubbles, ) ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt new file mode 100644 index 000000000000..6736ccf739ce --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted + +import android.app.Notification +import android.app.Notification.BigPictureStyle +import android.app.Notification.BigTextStyle +import android.app.Notification.CallStyle +import android.app.Notification.MessagingStyle +import android.app.Notification.ProgressStyle +import android.app.Notification.ProgressStyle.Segment +import android.app.PendingIntent +import android.app.Person +import android.content.Intent +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class PromotedNotificationContentExtractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + + private val provider = + FakePromotedNotificationsProvider().also { kosmos.promotedNotificationsProvider = it } + + private val underTest = kosmos.promotedNotificationContentExtractor + + @Test + @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun shouldNotExtract_bothFlagsDisabled() { + val notif = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(notif) + assertThat(content).isNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + fun shouldExtract_promotedNotificationUiFlagEnabled() { + val entry = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(entry) + assertThat(content).isNotNull() + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + fun shouldExtract_statusBarNotifChipsFlagEnabled() { + val entry = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(entry) + assertThat(content).isNotNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun shouldExtract_bothFlagsEnabled() { + val entry = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(entry) + assertThat(content).isNotNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun shouldNotExtract_providerDidNotPromote() { + val entry = createEntry().also { provider.promotedEntries.remove(it) } + val content = extractContent(entry) + assertThat(content).isNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_commonFields() { + val entry = + createEntry { + setSubText(TEST_SUB_TEXT) + setContentTitle(TEST_CONTENT_TITLE) + setContentText(TEST_CONTENT_TEXT) + } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.subText).isEqualTo(TEST_SUB_TEXT) + assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE) + assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromBigPictureStyle() { + val entry = + createEntry { setStyle(BigPictureStyle()) }.also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.BigPicture) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromBigTextStyle() { + val entry = + createEntry { setStyle(BigTextStyle()) }.also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.BigText) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromCallStyle() { + val hangUpIntent = + PendingIntent.getBroadcast(context, 0, Intent("hangup"), PendingIntent.FLAG_IMMUTABLE) + + val entry = + createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.Call) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromProgressStyle() { + val entry = + createEntry { + setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75)) + } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.Progress) + assertThat(content?.progress).isNotNull() + assertThat(content?.progress?.progress).isEqualTo(75) + assertThat(content?.progress?.progressMax).isEqualTo(100) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromIneligibleStyle() { + val entry = + createEntry { + setStyle( + MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON) + ) + } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.Ineligible) + } + + private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? { + val recoveredBuilder = Notification.Builder(context, entry.sbn.notification) + return underTest.extractContent(entry, recoveredBuilder) + } + + private fun createEntry(builderBlock: Notification.Builder.() -> Unit = {}): NotificationEntry { + val notif = Notification.Builder(context, "a").also(builderBlock).build() + return NotificationEntryBuilder().setNotification(notif).build() + } + + companion object { + private const val TEST_SUB_TEXT = "sub text" + private const val TEST_CONTENT_TITLE = "content title" + private const val TEST_CONTENT_TEXT = "content text" + + private const val TEST_PERSON_NAME = "person name" + private const val TEST_PERSON_KEY = "person key" + private val TEST_PERSON = + Person.Builder().setKey(TEST_PERSON_KEY).setName(TEST_PERSON_NAME).build() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt index 657e9df0029b..ca0f9ef5f2b0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.collection.provider.Notificat import com.android.systemui.statusbar.notification.collection.render.FakeNodeController import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer @@ -49,7 +50,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.SmartReplyConstants import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent import com.android.systemui.util.mockito.any diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java index 1c5f37cc60c3..979a1d0801f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java @@ -40,8 +40,9 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.shade.ShadeController; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import org.junit.Before; import org.junit.Test; @@ -91,7 +92,7 @@ public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase { ExpandableNotificationRowDragController controller = createSpyController(); mRow.setDragController(controller); mRow.setHeadsUp(true); - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); mRow.doLongClickCallback(0, 0); mRow.doDragCallback(0, 0); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index b278f1a48b3d..6eb2764165b5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -42,6 +43,7 @@ import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.TypedValue; @@ -56,8 +58,12 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.media.controls.util.MediaFeatureFlag; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -99,6 +105,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { @Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; @Mock private HeadsUpStyleProvider mHeadsUpStyleProvider; @Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; + @Mock private PromotedNotificationContentExtractor mPromotedNotificationContentExtractor; private final SmartReplyStateInflater mSmartReplyStateInflater = new SmartReplyStateInflater() { @@ -142,6 +149,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { mSmartReplyStateInflater, mNotifLayoutInflaterFactoryProvider, mHeadsUpStyleProvider, + mPromotedNotificationContentExtractor, mock(NotificationRowContentBinderLogger.class)); } @@ -382,6 +390,75 @@ public class NotificationContentInflaterTest extends SysuiTestCase { verify(mRow, times(0)).onNotificationUpdated(); } + @Test + @DisableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) + public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, never()).extractContent(any(), any()); + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + public void testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() + throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + } + + @Test + @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) + public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + } + + @Test + @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) + public void testExtractsPromotedContent_null() throws Exception { + when(mPromotedNotificationContentExtractor.extractContent(any(), any())).thenReturn(null); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertNull(mRow.getEntry().getPromotedNotificationContentModel()); + } + private static void inflateAndWait(NotificationContentInflater inflater, @InflationFlag int contentToInflate, ExpandableNotificationRow row) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt index b16d3ea5337b..6a0a5bb3b191 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt @@ -71,10 +71,10 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.wmshell.BubblesManager diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt index 48608ebd6de0..18517998096a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt @@ -21,6 +21,7 @@ import android.content.Context import android.os.AsyncTask import android.os.Build import android.os.CancellationSignal +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import android.util.TypedValue @@ -33,8 +34,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.ConversationNotificationProcessor import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED @@ -62,6 +67,7 @@ import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -82,7 +88,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { object : NotifLayoutInflaterFactory.Provider { override fun provide( row: ExpandableNotificationRow, - layoutType: Int + layoutType: Int, ): NotifLayoutInflaterFactory = mock() } private val smartReplyStateInflater: SmartReplyStateInflater = @@ -95,7 +101,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { notifPackageContext: Context, entry: NotificationEntry, existingSmartReplyState: InflatedSmartReplyState?, - newSmartReplyState: InflatedSmartReplyState + newSmartReplyState: InflatedSmartReplyState, ): InflatedSmartReplyViewHolder { return inflatedSmartReplies } @@ -104,6 +110,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { return inflatedSmartReplyState } } + private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor = mock() @Before fun setUp() { @@ -125,7 +132,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { smartReplyStateInflater, layoutInflaterFactoryProvider, mock<HeadsUpStyleProvider>(), - mock() + promotedNotificationContentExtractor, + mock(), ) } @@ -142,7 +150,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { FLAG_CONTENT_VIEW_ALL, builder, mContext, - smartReplyStateInflater + smartReplyStateInflater, + mock(), ) verify(builder).createHeadsUpContentView(true) } @@ -160,7 +169,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { FLAG_CONTENT_VIEW_ALL, builder, mContext, - smartReplyStateInflater + smartReplyStateInflater, + mock(), ) verify(builder).createContentView(true) } @@ -187,7 +197,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { true /* expectingException */, notificationInflater, FLAG_CONTENT_VIEW_ALL, - row + row, ) Assert.assertTrue(row.privateLayout.childCount == 0) verify(row, times(0)).onNotificationUpdated() @@ -210,7 +220,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { FLAG_CONTENT_VIEW_ALL, BindParams(), false /* forceInflate */, - null /* callback */ + null, /* callback */ ) Assert.assertNull(row.entry.runningTask) } @@ -223,7 +233,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { NotificationRowContentBinderImpl.InflationProgress( packageContext = mContext, remoteViews = NewRemoteViews(), - contentModel = NotificationContentModel(headsUpStatusBarModel) + contentModel = NotificationContentModel(headsUpStatusBarModel), + extractedPromotedNotificationContentModel = null, ) val countDownLatch = CountDownLatch(1) NotificationRowContentBinderImpl.applyRemoteView( @@ -261,7 +272,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { get() = AsyncFailRemoteView( mContext.packageName, - com.android.systemui.tests.R.layout.custom_view_dark + com.android.systemui.tests.R.layout.custom_view_dark, ) }, logger = mock(), @@ -280,7 +291,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { val decoratedMediaView = builder.createContentView() Assert.assertFalse( "The decorated media style doesn't allow a view to be reapplied!", - NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView) + NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView), ) } @@ -304,7 +315,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { Assert.assertEquals( "Binder inflated a new view even though the old one was cached and usable.", view, - row.privateLayout.contractedChild + row.privateLayout.contractedChild, ) } @@ -327,7 +338,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { Assert.assertNotEquals( "Binder (somehow) used the same view when inflating.", view, - row.privateLayout.contractedChild + row.privateLayout.contractedChild, ) } @@ -396,7 +407,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { private fun getValidationError( measuredHeightDp: Float, targetSdk: Int, - contentView: RemoteViews? + contentView: RemoteViews?, ): String? { val view: View = mock() whenever(view.measuredHeight) @@ -404,7 +415,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { TypedValue.applyDimension( COMPLEX_UNIT_SP, measuredHeightDp, - mContext.resources.displayMetrics + mContext.resources.displayMetrics, ) .toInt() ) @@ -419,7 +430,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { row.entry.sbn.notification.contentView = RemoteViews( mContext.packageName, - com.android.systemui.tests.R.layout.invalid_notification_height + com.android.systemui.tests.R.layout.invalid_notification_height, ) inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, row) Assert.assertEquals(0, row.privateLayout.childCount.toLong()) @@ -451,7 +462,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { false, notificationInflater, FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, - messagingRow + messagingRow, ) Assert.assertNotNull(messagingRow.publicLayout.mSingleLineView) // assert this is the conversation layout @@ -460,6 +471,59 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { ) } + @Test + @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun testExtractsPromotedContent_notWhenBothFlagsDisabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, never()).extractContent(any(), any()) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any()) + Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any()) + Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun testExtractsPromotedContent_whenBothFlagsEnabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any()) + Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + } + private class ExceptionHolder { var exception: Exception? = null } @@ -476,7 +540,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { parent: ViewGroup, executor: Executor, listener: OnViewAppliedListener, - handler: InteractionHandler? + handler: InteractionHandler?, ): CancellationSignal { executor.execute { listener.onError(RuntimeException("Failed to inflate async")) } return CancellationSignal() @@ -486,7 +550,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { context: Context, parent: ViewGroup, executor: Executor, - listener: OnViewAppliedListener + listener: OnViewAppliedListener, ): CancellationSignal { return applyAsync(context, parent, executor, listener, null) } @@ -496,7 +560,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { private fun inflateAndWait( inflater: NotificationRowContentBinderImpl, @InflationFlag contentToInflate: Int, - row: ExpandableNotificationRow + row: ExpandableNotificationRow, ) { inflateAndWait(false /* expectingException */, inflater, contentToInflate, row) } @@ -535,7 +599,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { contentToInflate, BindParams(), false /* forceInflate */, - callback /* callback */ + callback, /* callback */ ) Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS)) exceptionHolder.exception?.let { throw it } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 2340d0289db4..080ac3f8c697 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -82,13 +82,14 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.icon.IconBuilder; import com.android.systemui.statusbar.notification.icon.IconManager; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; import com.android.systemui.statusbar.policy.SmartReplyConstants; @@ -204,6 +205,7 @@ public class NotificationTestHelper { new MockSmartReplyInflater(), mock(NotifLayoutInflaterFactory.Provider.class), mock(HeadsUpStyleProvider.class), + mock(PromotedNotificationContentExtractor.class), mock(NotificationRowContentBinderLogger.class)) : new NotificationContentInflater( mock(NotifRemoteViewCache.class), @@ -214,6 +216,7 @@ public class NotificationTestHelper { new MockSmartReplyInflater(), mock(NotifLayoutInflaterFactory.Provider.class), mock(HeadsUpStyleProvider.class), + mock(PromotedNotificationContentExtractor.class), mock(NotificationRowContentBinderLogger.class)); contentBinder.setInflateSynchronously(true); mBindStage = new RowContentBindStage(contentBinder, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt index 07935e47845e..92b8c3aa84d6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt @@ -24,8 +24,8 @@ import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.andSceneContainer import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.headsup.AvalancheController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.policy.AvalancheController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 6425da46fc67..de40abb4d9d8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -83,7 +83,7 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -107,7 +107,7 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.ZenModeController; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index b877456ab604..2b7e95062716 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -27,11 +27,11 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView import com.android.systemui.statusbar.notification.footer.ui.view.FooterView import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState +import com.android.systemui.statusbar.notification.headsup.AvalancheController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.policy.AvalancheController import com.google.common.truth.Expect import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index bf144729dea3..e592e4b319e3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository import com.android.systemui.statusbar.policy.fakeConfigurationController @@ -522,21 +523,21 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas assertThat(pinnedHeadsUpRows).isEmpty() // WHEN a row gets pinned - rows[0].isPinned.value = true + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN it's added to the list assertThat(pinnedHeadsUpRows).containsExactly(rows[0]) // WHEN more rows are pinned - rows[1].isPinned.value = true + rows[1].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN they are all in the list assertThat(pinnedHeadsUpRows).containsExactly(rows[0], rows[1]) // WHEN a row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN it's removed from the list diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index d9e94953e757..f76f1ce48a5c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -57,7 +57,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 64414050e0f4..ad8b67513b9f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -53,7 +53,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index dd03ab393ce9..f9f2cd328094 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -45,13 +45,14 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.Clock; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -131,13 +132,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { @Test public void testShowinEntryUpdated() { - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertEquals(mRow.getEntry(), mHeadsUpStatusBarView.getShowingEntry()); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); assertEquals(null, mHeadsUpStatusBarView.getShowingEntry()); @@ -145,13 +146,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { @Test public void testShownUpdated() { - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertTrue(mHeadsUpAppearanceController.isShown()); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); Assert.assertFalse(mHeadsUpAppearanceController.isShown()); @@ -160,13 +161,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { @Test @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public void testHeaderUpdated() { - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertEquals(mRow.getHeaderVisibleAmount(), 0.0f, 0.0f); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); assertEquals(mRow.getHeaderVisibleAmount(), 1.0f, 0.0f); @@ -176,13 +177,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { public void testOperatorNameViewUpdated() { mHeadsUpAppearanceController.setAnimationsEnabled(false); - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility()); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); assertEquals(View.VISIBLE, mOperatorNameView.getVisibility()); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt index 0b0b1e45d604..90506a1b9a7f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt @@ -48,6 +48,11 @@ class MultiDisplayAutoHideControllerStoreTest : SysuiTestCase() { @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) } + @Before + fun start() { + underTest.start() + } + @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerTest.kt index a00858842742..d82cb86406ec 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerTest.kt @@ -41,12 +41,12 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -class StatusBarTouchableRegionManagerTest : SysuiTestCase() { +class ShadeTouchableRegionManagerTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sceneRepository = kosmos.sceneContainerRepository - private val underTest by Lazy { kosmos.statusBarTouchableRegionManager } + private val underTest by Lazy { kosmos.shadeTouchableRegionManager } @Test @EnableSceneContainer diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 0eb620343e6a..d174484219ff 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -190,6 +190,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor; @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; + @Mock + private KeyguardDismissActionInteractor mKeyguardDismissActionInteractor; @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -236,7 +238,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mKeyguardTransitionInteractor, mock(KeyguardDismissTransitionInteractor.class), StandardTestDispatcher(null, null), - () -> mock(KeyguardDismissActionInteractor.class), + () -> mKeyguardDismissActionInteractor, mSelectedUserInteractor, mock(JavaAdapter.class), () -> mSceneInteractor, @@ -968,4 +970,33 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { ); verify(mAlternateBouncerInteractor).hide(); } + + @Test + public void hideAlternateBouncer_clearsDismissActionByDefault() { + clearInvocations(mKeyguardDismissActionInteractor); + + mStatusBarKeyguardViewManager.hideAlternateBouncer(/* updateScrim= */ true); + + verify(mKeyguardDismissActionInteractor).clearDismissAction(); + } + + @Test + public void hideAlternateBouncer_clearsDismissActionExplicitly() { + clearInvocations(mKeyguardDismissActionInteractor); + + mStatusBarKeyguardViewManager.hideAlternateBouncer( + /* updateScrim= */ true, /* clearDismissAction= */ true); + + verify(mKeyguardDismissActionInteractor).clearDismissAction(); + } + + @Test + public void hideAlternateBouncer_doNotClearDismissActionExplicitly() { + clearInvocations(mKeyguardDismissActionInteractor); + + mStatusBarKeyguardViewManager.hideAlternateBouncer( + /* updateScrim= */ true, /* clearDismissAction= */ false); + + verify(mKeyguardDismissActionInteractor, never()).clearDismissAction(); + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 95472cad4b90..41782a123f14 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -73,7 +73,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt index a2fabf3b9baa..a2fabf3b9baa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt index b9cdd06de5a2..7b04fc827d83 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt @@ -28,8 +28,6 @@ import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -39,7 +37,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.collectValues +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.assertLogsWtf @@ -52,6 +52,7 @@ import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.shade.shadeTestUtil import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip @@ -66,15 +67,17 @@ import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationSt import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle import com.android.systemui.statusbar.notification.data.model.activeNotificationModel import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor +import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -83,35 +86,22 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class HomeStatusBarViewModelImplTest : SysuiTestCase() { - private val kosmos = - Kosmos().also { - it.testCase = this - it.testDispatcher = UnconfinedTestDispatcher() - } - - private val testScope = kosmos.testScope - - private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository - private val activeNotificationListRepository = kosmos.activeNotificationListRepository - private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository - private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository - private val systemStatusEventAnimationRepository = kosmos.systemStatusEventAnimationRepository - - private lateinit var underTest: HomeStatusBarViewModel + private val kosmos by lazy { + testKosmos().also { it.testDispatcher = UnconfinedTestDispatcher() } + } + private val Kosmos.underTest by Kosmos.Fixture { kosmos.homeStatusBarViewModel } @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - // Initialize here because some flags are checked when this class is constructed - underTest = kosmos.homeStatusBarViewModel } @Test fun isTransitioningFromLockscreenToOccluded_started_isTrue() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isTransitioningFromLockscreenToOccluded) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED, @@ -125,10 +115,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_running_isTrue() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isTransitioningFromLockscreenToOccluded) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED, @@ -142,10 +132,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_finished_isFalse() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isTransitioningFromLockscreenToOccluded) - keyguardTransitionRepository.sendTransitionSteps( + fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.OCCLUDED, testScope.testScheduler, @@ -156,10 +146,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_canceled_isFalse() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isTransitioningFromLockscreenToOccluded) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED, @@ -173,10 +163,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_irrelevantTransition_isFalse() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isTransitioningFromLockscreenToOccluded) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.AOD, KeyguardState.LOCKSCREEN, @@ -190,10 +180,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_followsRepoUpdates() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isTransitioningFromLockscreenToOccluded) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED, @@ -205,7 +195,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { assertThat(latest).isTrue() // WHEN the repo updates the transition to finished - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED, @@ -220,10 +210,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_started_emitted() = - testScope.runTest { + kosmos.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -237,10 +227,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_startedMultiple_emittedMultiple() = - testScope.runTest { + kosmos.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -249,7 +239,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { ) ) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -258,7 +248,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { ) ) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -272,10 +262,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_startedThenRunning_emittedOnlyOne() = - testScope.runTest { + kosmos.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -287,7 +277,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { // WHEN the transition progresses through its animation by going through the RUNNING // step with increasing fractions - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -296,7 +286,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { ) ) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -305,7 +295,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { ) ) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -321,10 +311,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransition_notEmitted() = - testScope.runTest { + kosmos.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED, @@ -338,10 +328,10 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransitionState_notEmitted() = - testScope.runTest { + kosmos.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) - keyguardTransitionRepository.sendTransitionStep( + fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, @@ -359,8 +349,8 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_lowProfileWithNotifications_true() = - testScope.runTest { - statusBarModeRepository.defaultDisplay.statusBarMode.value = + kosmos.runTest { + fakeStatusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT_TRANSPARENT activeNotificationListRepository.activeNotifications.value = activeNotificationsStore(testNotifications) @@ -373,8 +363,8 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_lowProfileWithoutNotifications_false() = - testScope.runTest { - statusBarModeRepository.defaultDisplay.statusBarMode.value = + kosmos.runTest { + fakeStatusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT_TRANSPARENT activeNotificationListRepository.activeNotifications.value = activeNotificationsStore(emptyList()) @@ -387,8 +377,9 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_defaultStatusBarModeWithoutNotifications_false() = - testScope.runTest { - statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + kosmos.runTest { + fakeStatusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.TRANSPARENT activeNotificationListRepository.activeNotifications.value = activeNotificationsStore(emptyList()) @@ -400,8 +391,9 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_defaultStatusBarModeWithNotifications_false() = - testScope.runTest { - statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + kosmos.runTest { + fakeStatusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.TRANSPARENT activeNotificationListRepository.activeNotifications.value = activeNotificationsStore(testNotifications) @@ -413,7 +405,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_requiresFlagEnabled() = - testScope.runTest { + kosmos.runTest { assertLogsWtf { val flow = underTest.areNotificationsLightsOut(DISPLAY_ID) assertThat(flow).isEqualTo(emptyFlow<Boolean>()) @@ -422,7 +414,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun primaryOngoingActivityChip_matchesViewModel() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.primaryOngoingActivityChip) kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording @@ -441,7 +433,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isHomeStatusBarAllowedByScene_sceneLockscreen_notOccluded_false() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) @@ -452,7 +444,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isHomeStatusBarAllowedByScene_sceneLockscreen_occluded_true() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) @@ -463,7 +455,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isHomeStatusBarAllowedByScene_sceneBouncer_false() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer) @@ -473,7 +465,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isHomeStatusBarAllowedByScene_sceneCommunal_false() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) kosmos.sceneContainerRepository.snapToScene(Scenes.Communal) @@ -483,7 +475,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isHomeStatusBarAllowedByScene_sceneShade_false() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) kosmos.sceneContainerRepository.snapToScene(Scenes.Shade) @@ -493,7 +485,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isHomeStatusBarAllowedByScene_sceneGone_true() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) kosmos.sceneContainerRepository.snapToScene(Scenes.Gone) @@ -503,35 +495,91 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isClockVisible_allowedByDisableFlags_visible() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isClockVisible) transitionKeyguardToGone() - disableFlagsRepository.disableFlags.value = + fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) } @Test - fun isClockVisible_notAllowedByDisableFlags_gone() = - testScope.runTest { + fun isClockVisible_notAllowedByDisableFlags_invisible() = + kosmos.runTest { val latest by collectLastValue(underTest.isClockVisible) transitionKeyguardToGone() - disableFlagsRepository.disableFlags.value = + fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_CLOCK, DISABLE2_NONE) - assertThat(latest!!.visibility).isEqualTo(View.GONE) + assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE) + } + + @Test + fun isClockVisible_allowedByFlags_hunActive_notVisible() = + kosmos.runTest { + val latest by collectLastValue(underTest.isClockVisible) + transitionKeyguardToGone() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + // there is an active HUN + headsUpNotificationRepository.setNotifications( + fakeHeadsUpRowRepository(isPinned = true) + ) + + assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE) + } + + @Test + fun isClockVisible_allowedByFlags_hunBecomesInactive_visibleAgain() = + kosmos.runTest { + val latest by collectLastValue(underTest.isClockVisible) + transitionKeyguardToGone() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + // there is an active HUN + headsUpNotificationRepository.setNotifications( + fakeHeadsUpRowRepository(isPinned = true) + ) + + // hun goes away + headsUpNotificationRepository.setNotifications(listOf()) + + assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) + } + + @Test + fun isClockVisible_disabledByFlags_hunBecomesInactive_neverVisible() = + kosmos.runTest { + val latest by collectLastValue(underTest.isClockVisible) + transitionKeyguardToGone() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_CLOCK, DISABLE2_NONE) + // there is an active HUN + headsUpNotificationRepository.setNotifications( + fakeHeadsUpRowRepository(isPinned = true) + ) + + assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE) + + // hun goes away + headsUpNotificationRepository.setNotifications(listOf()) + + assertThat(latest!!.visibility).isEqualTo(View.INVISIBLE) } @Test fun isNotificationIconContainerVisible_allowedByDisableFlags_visible() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isNotificationIconContainerVisible) transitionKeyguardToGone() - disableFlagsRepository.disableFlags.value = + fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) @@ -539,23 +587,55 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isNotificationIconContainerVisible_notAllowedByDisableFlags_gone() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.isNotificationIconContainerVisible) transitionKeyguardToGone() - disableFlagsRepository.disableFlags.value = + fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_NOTIFICATION_ICONS, DISABLE2_NONE) assertThat(latest!!.visibility).isEqualTo(View.GONE) } @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + fun isNotificationIconContainerVisible_anyChipShowing_PromotedNotifsOn() = + kosmos.runTest { + val latest by collectLastValue(underTest.isNotificationIconContainerVisible) + transitionKeyguardToGone() + + kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + + assertThat(latest!!.visibility).isEqualTo(View.GONE) + + kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing + + assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) + } + + @Test + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + fun isNotificationIconContainerVisible_anyChipShowing_PromotedNotifsOff() = + kosmos.runTest { + val latest by collectLastValue(underTest.isNotificationIconContainerVisible) + transitionKeyguardToGone() + + kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + + assertThat(latest!!.visibility).isEqualTo(View.GONE) + + kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing + + assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) + } + + @Test fun isSystemInfoVisible_allowedByDisableFlags_visible() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() - disableFlagsRepository.disableFlags.value = + fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.VISIBLE) @@ -563,11 +643,11 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isSystemInfoVisible_notAllowedByDisableFlags_gone() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() - disableFlagsRepository.disableFlags.value = + fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_SYSTEM_INFO, DISABLE2_NONE) assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.GONE) @@ -575,7 +655,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun systemInfoCombineVis_animationsPassThrough() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() @@ -601,18 +681,18 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableSceneContainer fun lockscreenVisible_sceneFlagOff_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) - keyguardTransitionRepository.sendTransitionSteps( + fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN, - testScope = this, + testScope = testScope, ) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @@ -620,14 +700,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableSceneContainer fun lockscreenVisible_sceneFlagOn_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @@ -635,18 +715,18 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableSceneContainer fun bouncerVisible_sceneFlagOff_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) - keyguardTransitionRepository.sendTransitionSteps( + fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.PRIMARY_BOUNCER, - testScope = this, + testScope = testScope, ) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @@ -654,14 +734,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableSceneContainer fun bouncerVisible_sceneFlagOn_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @@ -669,15 +749,15 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableSceneContainer fun keyguardIsOccluded_sceneFlagOff_statusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) - keyguardTransitionRepository.sendTransitionSteps( + fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.OCCLUDED, - testScope = this, + testScope = testScope, ) assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE) @@ -688,7 +768,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableSceneContainer fun keyguardIsOccluded_sceneFlagOn_statusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) @@ -704,7 +784,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableSceneContainer fun keyguardNotShown_sceneFlagOff_statusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) @@ -719,7 +799,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableSceneContainer fun shadeNotShown_sceneFlagOff_statusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) @@ -735,7 +815,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableSceneContainer fun keyguardNotShownAndShadeNotShown_sceneFlagOn_statusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) @@ -750,7 +830,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableSceneContainer fun shadeShown_sceneFlagOff_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) @@ -758,7 +838,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { kosmos.shadeTestUtil.setShadeExpansion(1f) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @@ -766,7 +846,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableSceneContainer fun shadeShown_sceneFlagOn_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) @@ -774,7 +854,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { kosmos.sceneContainerRepository.snapToScene(Scenes.Shade) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @@ -782,20 +862,20 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @DisableSceneContainer fun secureCameraActive_sceneFlagOff_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) // Secure camera is an occluding activity - keyguardTransitionRepository.sendTransitionSteps( + fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.OCCLUDED, - testScope = this, + testScope = testScope, ) kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @@ -803,7 +883,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test @EnableSceneContainer fun secureCameraActive_sceneFlagOn_noStatusBarViewsShown() = - testScope.runTest { + kosmos.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) @@ -813,21 +893,26 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null) kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) - assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } + // Cribbed from [HeadsUpNotificationInteractorTest.kt] + private fun fakeHeadsUpRowRepository(key: String = "test key", isPinned: Boolean = false) = + FakeHeadsUpRowRepository(key = key, isPinned = isPinned) + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = ActiveNotificationsStore.Builder() .apply { notifications.forEach(::addIndividualNotif) } .build() - private val testNotifications = + private val testNotifications by lazy { listOf(activeNotificationModel(key = "notif1"), activeNotificationModel(key = "notif2")) + } - private suspend fun transitionKeyguardToGone() { - keyguardTransitionRepository.sendTransitionSteps( + private suspend fun Kosmos.transitionKeyguardToGone() { + fakeKeyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE, testScope = testScope, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt index 7d3ed92cecc6..7aa389a1739f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt @@ -20,10 +20,12 @@ import android.view.MotionEvent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE +import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -33,12 +35,24 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class HomeGestureRecognizerTest : SysuiTestCase() { + companion object { + const val THRESHOLD_VELOCITY_PX_PER_MS = 1f + const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f + const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f + } + private var gestureState: GestureState = GestureState.NotStarted + private var velocityTracker = testKosmos().fakeVelocityTracker private val gestureRecognizer = - HomeGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt()) + HomeGestureRecognizer( + gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(), + velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS, + velocityTracker = velocityTracker, + ) @Before fun before() { + velocityTracker.setVelocity(Velocity(FAST)) gestureRecognizer.addGestureStateCallback { gestureState = it } } @@ -48,6 +62,12 @@ class HomeGestureRecognizerTest : SysuiTestCase() { } @Test + fun doesntTriggerGestureFinished_onGestureSpeedTooSlow() { + velocityTracker.setVelocity(Velocity(SLOW)) + assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted) + } + + @Test fun triggersGestureProgressForThreeFingerGestureStarted() { assertStateAfterEvents( events = ThreeFingerGesture.startEvents(x = 0f, y = 0f), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt index c5c0d59ea48b..cb74e6569b3f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt @@ -17,21 +17,19 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.view.MotionEvent -import androidx.compose.ui.input.pointer.util.VelocityTracker1D import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE +import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @@ -39,26 +37,23 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { companion object { const val THRESHOLD_VELOCITY_PX_PER_MS = 0.1f - // multiply by 1000 to get px/ms instead of px/s which is unit used by velocity tracker - const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS * 1000 - 1 - const val FAST = THRESHOLD_VELOCITY_PX_PER_MS * 1000 + 1 + const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f + const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f } + private var velocityTracker = testKosmos().fakeVelocityTracker + private var gestureState: GestureState = GestureState.NotStarted - private val velocityTracker1D = - mock<VelocityTracker1D> { - // by default return correct speed for the gesture - as if pointer is slowing down - on { calculateVelocity() } doReturn SLOW - } private val gestureRecognizer = RecentAppsGestureRecognizer( gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(), velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS, - velocityTracker = VerticalVelocityTracker(velocityTracker1D), + velocityTracker = velocityTracker, ) @Before fun before() { + velocityTracker.setVelocity(Velocity(SLOW)) gestureRecognizer.addGestureStateCallback { gestureState = it } } @@ -69,7 +64,7 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { @Test fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() { - whenever(velocityTracker1D.calculateVelocity()).thenReturn(FAST) + velocityTracker.setVelocity(Velocity(FAST)) assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt b/packages/SystemUI/multivalentTests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt index 5146f77bbcf6..5146f77bbcf6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt +++ b/packages/SystemUI/multivalentTests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp index 6212e2be4911..2cd334693486 100644 --- a/packages/SystemUI/plugin/Android.bp +++ b/packages/SystemUI/plugin/Android.bp @@ -55,6 +55,8 @@ java_library { "SystemUICommon", "SystemUILogLib", "androidx.annotation_annotation", + "androidx.compose.ui_ui", + "androidx.compose.runtime_runtime", ], } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/AuthContextPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/AuthContextPlugin.kt new file mode 100644 index 000000000000..773c2a2d5e78 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/AuthContextPlugin.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.plugins + +import android.os.IBinder +import android.view.View +import com.android.systemui.plugins.annotations.ProvidesInterface + +/** + * Plugin for experimental "Contextual Auth" features. + * + * These plugins will get raw access to low-level events about the user's environment, such as + * moving in/out of trusted locations, connection status of trusted devices, auth attempts, etc. + * They will also receive callbacks related to system events & transitions to enable prototypes on + * sensitive surfaces like lock screen and BiometricPrompt. + * + * Note to rebuild the plugin jar run: m PluginDummyLib + */ +@ProvidesInterface(action = AuthContextPlugin.ACTION, version = AuthContextPlugin.VERSION) +interface AuthContextPlugin : Plugin { + + /** + * Called in the background when the plugin is enabled. + * + * This is a good time to ask your friendly [saucier] to cook up something special. The + * [Plugin.onCreate] can also be used for initialization. + */ + fun activated(saucier: Saucier) + + /** + * Called when a [SensitiveSurface] is first shown. + * + * This may be called repeatedly if the state of the surface changes after it is shown. For + * example, [SensitiveSurface.BiometricPrompt.isCredential] will change if the user falls back + * to a credential-based auth method. + */ + fun onShowingSensitiveSurface(surface: SensitiveSurface) + + /** + * Called when a [SensitiveSurface] sensitive surface is hidden. + * + * This method may still be called without [onShowingSensitiveSurface] in cases of rapid + * dismissal and plugins implementations should typically be idempotent. + */ + fun onHidingSensitiveSurface(surface: SensitiveSurface) + + companion object { + /** Plugin action. */ + const val ACTION = "com.android.systemui.action.PLUGIN_AUTH_CONTEXT" + /** Plugin version. */ + const val VERSION = 1 + } + + /** Information about a sensitive surface in the framework, which the Plugin may augment. */ + sealed interface SensitiveSurface { + + /** Information about the BiometricPrompt that is being shown to the user. */ + data class BiometricPrompt(val view: View? = null, val isCredential: Boolean = false) : + SensitiveSurface + + /** Information about bouncer. */ + data class LockscreenBouncer(val view: View? = null) : SensitiveSurface + } + + /** Ask for the special. */ + interface Saucier { + + /** What [flavor] would you like? */ + fun getSauce(flavor: String): IBinder? + } +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java index 73626b457dcf..e3cbd6643e5f 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java @@ -120,6 +120,11 @@ public interface QSTile { */ boolean isListening(); + /** + * Return this tile's {@link TileDetailsViewModel} to be used to render the TileDetailsView. + */ + default TileDetailsViewModel getDetailsViewModel() { return null; } + @ProvidesInterface(version = Callback.VERSION) interface Callback { static final int VERSION = 2; diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt new file mode 100644 index 000000000000..eab7d7913129 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.plugins.qs + +import androidx.compose.runtime.Composable + +/** + * The base view model class for rendering the Tile's TileDetailsView. + */ +abstract class TileDetailsViewModel { + + // The view content of this tile details view. + @Composable + abstract fun GetContentView() + + // The callback when the settings button is clicked. Currently this is the same as the on tile + // long press callback + abstract fun clickOnSettingsButton() + + abstract fun getTitle(): String + + abstract fun getSubTitle(): String +} diff --git a/packages/SystemUI/res/layout/audio_sharing_dialog.xml b/packages/SystemUI/res/layout/audio_sharing_dialog.xml index 7534e159beb0..014b7f7b45df 100644 --- a/packages/SystemUI/res/layout/audio_sharing_dialog.xml +++ b/packages/SystemUI/res/layout/audio_sharing_dialog.xml @@ -84,7 +84,7 @@ android:id="@+id/share_audio_button" style="@style/SettingsLibActionButton" android:textColor="?androidprv:attr/textColorOnAccent" - android:background="@drawable/audio_sharing_rounded_bg_ripple" + android:background="@drawable/audio_sharing_rounded_bg_ripple_top" android:layout_marginBottom="4dp" android:layout_width="0dp" android:layout_height="wrap_content" @@ -101,7 +101,7 @@ android:id="@+id/switch_active_button" style="@style/SettingsLibActionButton" android:textColor="?androidprv:attr/textColorOnAccent" - android:background="@drawable/audio_sharing_rounded_bg_ripple" + android:background="@drawable/audio_sharing_rounded_bg_ripple_bottom" android:layout_marginBottom="20dp" android:layout_width="0dp" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml index d8967d4706ce..e90471e75287 100644 --- a/packages/SystemUI/res/layout/controls_management.xml +++ b/packages/SystemUI/res/layout/controls_management.xml @@ -22,7 +22,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" - android:paddingTop="@dimen/controls_management_top_padding" android:paddingStart="@dimen/controls_management_side_padding" android:paddingEnd="@dimen/controls_management_side_padding" > diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml index ff5d9d3567f6..a338e4c70cfa 100644 --- a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml +++ b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml @@ -29,16 +29,18 @@ android:layout_height="@dimen/notification_2025_single_line_face_pile_size" android:layout_marginEnd="8dp" > - <ImageView + <com.android.internal.widget.CachingIconView android:id="@*android:id/conversation_icon" android:layout_width="@dimen/notification_2025_single_line_avatar_size" android:layout_height="@dimen/notification_2025_single_line_avatar_size" android:layout_gravity="center_vertical|end" + android:background="@*android:drawable/notification_icon_circle" + android:clipToOutline="true" /> <ViewStub android:id="@*android:id/conversation_face_pile" - android:layout="@*android:layout/conversation_face_pile_layout" + android:layout="@*android:layout/notification_2025_conversation_face_pile_layout" android:layout_width="@dimen/notification_2025_single_line_face_pile_size" android:layout_height="@dimen/notification_2025_single_line_face_pile_size" android:layout_gravity="center_vertical|end" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index ebee1576342a..f095c0e14271 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sluitskermlegstukke"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Om ’n app met ’n legstuk oop te maak, sal jy moet verifieer dat dit jy is. Hou ook in gedagte dat enigeen dit kan bekyk, selfs wanneer jou tablet gesluit is. Sommige legstukke is moontlik nie vir jou sluitskerm bedoel nie en dit kan onveilig wees om dit hier by te voeg."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Het dit"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Word aan die bokant van gesprekskennisgewings en as \'n profielfoto op sluitskerm gewys, verskyn as \'n borrel, onderbreek Moenie Steur Nie"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> steun nie gesprekskenmerke nie"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Hierdie kennisgewings kan nie gewysig word nie."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Oproepkennisgewings kan nie gewysig word nie."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Hierdie groep kennisgewings kan nie hier opgestel word nie"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Keer die foon om vir hoër resolusie"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Voubare toestel word ontvou"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Voubare toestel word omgekeer"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Voorste skerm is aangeskakel"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gevou"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"oopgevou"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pasmaak kortpadsleutels"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Druk sleutel om kortpad toe te wys"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen soekresultate nie"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Sleutelbordinstellings"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stel kortpad"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselleer"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk sleutel"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Sleutelkombinasie is reeds in gebruik. Probeer ’n ander sleutel."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeer met jou sleutelbord"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer kortpadsleutels"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeer met jou raakpaneel"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 0351c7508703..0bd9f1ded56c 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"የማያ ገፅ ቁልፍ ምግብሮች"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ምግብር በመጠቀም መተግበሪያ ለመክፈት እርስዎ መሆንዎን ማረጋገጥ አለብዎት። እንዲሁም የእርስዎ ጡባዊ በተቆለፈበት ጊዜ እንኳን ማንኛውም ሰው እነሱን ማየት እንደሚችል ከግምት ውስጥ ያስገቡ። አንዳንድ ምግብሮች ለማያ ገፅ ቁልፍዎ የታሰቡ ላይሆኑ ይችላሉ እና እዚህ ለማከል አስተማማኝ ላይሆኑ ይችላሉ።"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ገባኝ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"በውይይት ማሳወቂያዎች አናት ላይ እና በማያ ገፅ መቆለፊያ ላይ እንደ መገለጫ ምስል ይታያል፣ እንደ አረፋ ሆኖ ይታያል፣ አትረብሽን ያቋርጣል"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ቅድሚያ"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> የውይይት ባህሪያትን አይደግፍም"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"እነዚህ ማሳወቂያዎች ሊሻሻሉ አይችሉም።"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"የጥሪ ማሳወቂያዎች ሊቀየሩ አይችሉም።"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"የማሳወቂያዎች ይህ ቡድን እዚህ ላይ ሊዋቀር አይችልም"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ለከፍተኛ ጥራት ስልኩን ይቀይሩ"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"መታጠፍ የሚችል መሣሪያ እየተዘረጋ ነው"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"መታጠፍ የሚችል መሣሪያ እየተገለበጠ ነው"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"የፊት ለፊት ማያ ገፅ በርቷል"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"የታጠፈ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"የተዘረጋ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ተደራሽነት"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"የቁልፍ ሰሌዳ አቋራጮች"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"የቁልፍ ሰሌዳ አቋራጮችን ያብጁ"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"አቋራጭ ለመመደብ ቁልፍ ይጫኑ"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ምንም የፍለጋ ውጤቶች የሉም"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"የቁልፍ ሰሌዳ ቅንብሮች"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"አቋራጭ አቀናብር"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ይቅር"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ቁልፍ ይጫኑ"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"የቁልፍ ጥምረት አስቀድሞ በሥራ ላይ ነው። ሌላ ቁልፍ ይሞክሩ።"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"የቁልፍ ሰሌዳዎን በመጠቀም ያስሱ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"የቁልፍ ሰሌዳ አቋራጮችን ይወቁ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"የመዳሰሻ ሰሌዳዎን በመጠቀም ያስሱ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 645d88008b21..bdd6139ddf18 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -113,7 +113,7 @@ <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"تسجيل شاشة تطبيق واحد"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"تسجيل الشاشة بكاملها"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"تسجيل محتوى الشاشة بالكامل: %s"</string> - <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"أثناء تسجيل محتوى الشاشة بالكامل، يتم تسجيل كل المحتوى المعروض على شاشتك. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string> + <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"أثناء تسجيل محتوى الشاشة بالكامل، يتم تسجيل كل المحتوى المعروض على شاشتك، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string> <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"سيتم تسجيل كل المحتوى المعروض أو المشغَّل على شاشة التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string> <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"تسجيل الشاشة"</string> <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"يُرجى اختيار تطبيق لتسجيل شاشته"</string> @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"لفتح تطبيق باستخدام تطبيق مصغَّر، عليك إثبات هويتك. يُرجى ملاحظة أنّ أي شخص يمكنه الاطّلاع محتوى التطبيقات المصغَّرة، حتى وإن كان جهازك اللوحي مُقفلاً. بعض التطبيقات المصغّرة قد لا تكون مُصمَّمة لإضافتها إلى شاشة القفل، وقد يكون هذا الإجراء غير آمن."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"حسنًا"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string> @@ -695,10 +699,10 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. انقر للتعيين على الاهتزاز."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. انقر لكتم الصوت."</string> - <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"التحكُّم في مستوى الضوضاء"</string> + <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"التحكّم بالضوضاء"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"الصوت المكاني"</string> - <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"غير مفعّل"</string> - <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"تفعيل"</string> + <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"عدم التفعيل"</string> + <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"التفعيل بدون تتبّع"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"تتبُّع حركة الرأس"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"انقر لتغيير وضع الرنين."</string> <string name="volume_ringer_mode" msgid="6867838048430807128">"وضع الرنين"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"تظهر في أعلى إشعارات المحادثات وكصورة ملف شخصي على شاشة القفل وتظهر على شكل فقاعة لمقاطعة ميزة \"عدم الإزعاج\"."</string> <string name="notification_priority_title" msgid="2079708866333537093">"الأولوية"</string> <string name="no_shortcut" msgid="8257177117568230126">"لا يدعم تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> ميزات المحادثات."</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"يتعذّر تعديل هذه الإشعارات."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"لا يمكن تعديل إشعارات المكالمات."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"يتعذّر ضبط مجموعة الإشعارات هذه هنا."</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"يجب استخدام أقل من <xliff:g id="LENGTH">%1$d</xliff:g> حرف."</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"رقم الإصدار"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"تم نسخ رقم الإصدار إلى الحافظة."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"نسخ المحتوى إلى الحافظة"</string> <string name="basic_status" msgid="2315371112182658176">"محادثة مفتوحة"</string> <string name="select_conversation_title" msgid="6716364118095089519">"التطبيقات المصغّرة للمحادثات"</string> <string name="select_conversation_text" msgid="3376048251434956013">"انقر على محادثة لإضافتها إلى \"الشاشة الرئيسية\""</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"للحصول على درجة دقة أعلى، اقلِب الهاتف."</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"جهاز قابل للطي يجري فتحه"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"جهاز قابل للطي يجري قلبه"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"تم تفعيل الشاشة الأمامية"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"مطوي"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"غير مطوي"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"تسهيل الاستخدام"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"اختصارات لوحة المفاتيح"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"تخصيص اختصارات لوحة المفاتيح"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"اضغط على مفتاح لتخصيص الاختصار"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"البحث في الاختصارات"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ما مِن نتائج بحث"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"مقبض السحب"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"إعدادات لوحة المفاتيح"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ضبط الاختصار"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"إلغاء"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"اضغط على مفتاح"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"يتم حاليًا استخدام مجموعة المفاتيح هذه. يُرجى تجربة مفتاح آخر."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"التنقّل باستخدام لوحة المفاتيح"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"تعرَّف على اختصارات لوحة المفاتيح"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"التنقّل باستخدام لوحة اللمس"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index e14f4ccdece6..631c43dda9f1 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্ৰীন ৱিজেট"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"এটা ৱিজেট ব্যৱহাৰ কৰি কোনো এপ্ খুলিবলৈ, এয়া আপুনিয়েই বুলি সত্যাপন পৰীক্ষা কৰিব লাগিব। লগতে, মনত ৰাখিব যে যিকোনো লোকেই সেইবোৰ চাব পাৰে, আনকি আপোনাৰ টেবলেটটো লক হৈ থাকিলেও। কিছুমান ৱিজেট হয়তো আপোনাৰ লক স্ক্ৰীনৰ বাবে কৰা হোৱা নাই আৰু ইয়াত যোগ কৰাটো অসুৰক্ষিত হ’ব পাৰে।"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুজি পালোঁ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, এটা বাবল হিচাপে দেখা পোৱা যায়, অসুবিধা নিদিব ম’ডত ব্যাঘাত জন্মায়"</string> <string name="notification_priority_title" msgid="2079708866333537093">"অগ্ৰাধিকাৰ"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বাৰ্তালাপৰ সুবিধাসমূহ সমৰ্থন নকৰে"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"এই জাননীসমূহ সংশোধন কৰিব নোৱাৰি।"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"কলৰ জাননীসমূহ সংশোধন কৰিব নোৱাৰি।"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"এই ধৰণৰ জাননীবোৰ ইয়াত কনফিগাৰ কৰিব পৰা নাযায়"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউশ্বনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"সন্মুখৰ স্ক্ৰীনখন অন কৰা হৈছে"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফ’ল্ড কৰা"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"আনফ’ল্ড কৰা"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"সাধ্য সুবিধা"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীব’ৰ্ডৰ শ্বৰ্টকাট কাষ্টমাইজ কৰক"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"শ্বৰ্টকাটৰ ভূমিকা অৰ্পণ কৰিবলৈ কী টিপক"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"সন্ধানৰ কোনো ফলাফল নাই"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীব’ৰ্ডৰ ছেটিং"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"শ্বৰ্টকাট ছেট কৰক"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল কৰক"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী টিপক"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"কীৰ মিশ্ৰণ ইতিমধ্যে ব্যৱহাৰ হৈ আছে। অন্য এটা কী ব্যৱহাৰ কৰি চাওক।"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"কীব’ৰ্ড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীব’ৰ্ডৰ শ্বৰ্টকাটসমূহৰ বিষয়ে জানক"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 90168703b896..2b61f0162dbf 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilid ekranı vidcetləri"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Vidcetdən istifadə edərək tətbiqi açmaq üçün kimliyi doğrulamalısınız. Planşet kilidli olsa da, hər kəs vidcetlərə baxa bilər. Bəzi vidcetlər kilid ekranı üçün nəzərdə tutulmayıb və bura əlavə etmək təhlükəli ola bilər."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Söhbət bildirişlərinin yuxarısında və kilid ekranında profil şəkli kimi göstərilir, baloncuq kimi görünür, Narahat Etməyin rejimini kəsir"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> söhbət funksiyalarını dəstəkləmir"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Bu bildirişlər dəyişdirilə bilməz."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Zəng bildirişləri dəyişdirilə bilməz."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Bu bildiriş qrupunu burada konfiqurasiya etmək olmaz"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Daha yüksək ayırdetmə dəqiqliyi üçün telefonu çevirin"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Qatlana bilən cihaz açılır"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Qatlana bilən cihaz fırladılır"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Ön ekran aktiv edildi"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"qatlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"açıq"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatura qısayollarını fərdiləşdirin"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Qısayol təyin etmək üçün düyməni basın"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Axtarış nəticəsi yoxdur"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dəstəyi çəkin"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura ayarları"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Qısayol ayarlayın"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ləğv edin"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Düyməni basın"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Düymə kombinasiyası artıq istifadə olunur. Başqa düyməni sınayın."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviaturadan istifadə edərək hərəkət edin"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klaviatura qısayolları haqqında öyrənin"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Taçpeddən istifadə edərək hərəkət edin"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index d2636c050db0..c52824f8e487 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti za zaključani ekran"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju koja koristi vidžet, treba da potvrdite da ste to vi. Imajte u vidu da svako može da ga vidi, čak i kada je tablet zaključan. Neki vidžeti možda nisu namenjeni za zaključani ekran i možda nije bezbedno da ih tamo dodate."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Važi"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikazuje se u vrhu obaveštenja o konverzacijama i kao slika profila na zaključanom ekranu, pojavljuje se kao oblačić, prekida režim Ne uznemiravaj"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetno"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava funkcije konverzacije"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ova obaveštenja ne mogu da se menjaju."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Obaveštenja o pozivima ne mogu da se menjaju."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ova grupa obaveštenja ne može da se konfiguriše ovde"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Koristite manji broj znakova od <xliff:g id="LENGTH">%1$d</xliff:g>"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"kopirajte u privremenu memoriju."</string> <string name="basic_status" msgid="2315371112182658176">"Otvorite konverzaciju"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Vidžeti za konverzaciju"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Dodirnite konverzaciju da biste je dodali na početni ekran"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Za veću rezoluciju obrnite telefon"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Uređaj na preklop se otvara"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Uređaj na preklop se obrće"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Prednji ekran je uključen"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite tasterske prečice"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pritisnite taster da biste dodelili prečicu"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pretražite prečice"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretrage"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za prevlačenje"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Podešavanja tastature"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Podesi prečicu"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Otkaži"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite taster"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinacija tastera se već koristi. Probajte sa drugim tasterom."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tasterskim prečicama"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću tačpeda"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 388950cb7bdd..a58b47cc3b1b 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджэты на экране блакіроўкі"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Каб адкрыць праграму з дапамогай віджэта, вам неабходна будзе пацвердзіць сваю асобу. Таксама памятайце, што такія віджэты могуць пабачыць іншыя людзі, нават калі экран планшэта заблакіраваны. Некаторыя віджэты могуць не падыходзіць для выкарыстання на экране блакіроўкі, і дадаваць іх сюды можа быць небяспечна."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Зразумела"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"З’яўляецца ўверсе раздзела размоў як усплывальнае апавяшчэнне, якое перарывае рэжым \"Не турбаваць\" і паказвае на экране блакіроўкі відарыс профілю"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Прыярытэт"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не падтрымлівае функцыі размовы"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Гэтыя апавяшчэнні нельга змяніць."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Апавяшчэнні пра выклікі нельга змяніць."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Тут канфігурыраваць гэту групу апавяшчэнняў забаронена"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Каб зрабіць фота з больш высокай раздзяляльнасцю, павярніце тэлефон"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Складная прылада ў раскладзеным выглядзе"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Перавернутая складная прылада"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Пярэдні экран уключаны"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складзена"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"раскладзена"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Спецыяльныя магчымасці"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Спалучэнні клавіш"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Наладзіць спалучэнні клавіш"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Націсніце клавішу, каб прызначыць спалучэнне клавіш"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма вынікаў пошуку"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налады клавіятуры"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Наладзіць спалучэнне клавіш"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасаваць"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Націсніце клавішу"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Гэта спалучэнне клавіш ужо выкарыстоўваецца. Паспрабуйце іншую клавішу."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігацыя з дапамогай клавіятуры"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Азнаёмцеся са спалучэннямі клавіш"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігацыя з дапамогай сэнсарнай панэлі"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 2d68f9190a30..e8478053bc1e 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Приспособления за заключения екран"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Показва се в горната част на известията за разговори и като снимка на потребителския профил на заключения екран, изглежда като балонче, прекъсва режима „Не безпокойте“"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддържа функциите за разговор"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Тези известия не могат да бъдат променяни."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Известията за обаждания не могат да бъдат променяни."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Тази група от известия не може да бъде конфигурирана тук"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Използвайте по-малко от <xliff:g id="LENGTH">%1$d</xliff:g> знака"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер на компилацията"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Номерът на компилацията е копиран в буферната памет."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"копиране в буферната памет."</string> <string name="basic_status" msgid="2315371112182658176">"Отворен разговор"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Приспособления за разговор"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Докоснете разговор, за да го добавите към началния си екран"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"За по-висока разделителна способност обърнете телефона"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Разгъване на сгъваемо устройство"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Обръщане на сгъваемо устройство"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Предният екран е включен"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Достъпност"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Персонализиране на клавишните комбинации"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Натиснете клавиш, за да зададете клавишна комбинация"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма резултати от търсенето"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки на клавиатурата"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Задаване на клавишна комбинация"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отказ"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натиснете клавиш"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Клавишната комбинация вече се използва. Опитайте с друг клавиш."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигирайте посредством клавиатурата си"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете за клавишните комбинации"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигирайте посредством сензорния панел"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 4281ea111b03..557edbefab47 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্রিন উইজেট"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string> <string name="notification_priority_title" msgid="2079708866333537093">"অগ্রাধিকার"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এ কথোপকথন ফিচার কাজ করে না"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"এই বিজ্ঞপ্তিগুলি পরিবর্তন করা যাবে না।"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"কল বিজ্ঞপ্তি পরিবর্তন করা যাবে না।"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"এই সমস্ত বিজ্ঞপ্তিকে এখানে কনফিগার করা যাবে না"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"আরও বেশি রেজোলিউশনের জন্য, ফোন ফ্লিপ করুন"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ফোল্ড করা যায় এমন ডিভাইস খোলা হচ্ছে"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ফোল্ড করা যায় এমন ডিভাইস উল্টানো হচ্ছে"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ফ্রন্ট স্ক্রিন চালু আছে"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফোল্ড করা রয়েছে"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ফোল্ড করা নেই"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"অ্যাক্সেসিবিলিটি"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীবোর্ড শর্টকাট কাস্টমাইজ করুন"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"শর্টকাট অ্যাসাইন করতে কী প্রেস করুন"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"শর্টকাট সার্চ করুন"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"কোনও সার্চ ফলাফল নেই"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীবোর্ড সেটিংস"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"শর্টকাট সেট করুন"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল করুন"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী প্রেস করুন"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"কী কম্বিনেশন আগে থেকে ব্যবহার হচ্ছে। অন্য কী ব্যবহার করে দেখুন।"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"আপনার কীবোর্ড ব্যবহার করে নেভিগেট করুন"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীবোর্ড শর্টকাট সম্পর্কে জানুন"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপনার টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 9d9bf7f004a2..ac4099f169d5 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti na zaključanom ekranu"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikazuje se na vrhu obavještenja u razgovorima i kao slika profila na zaključanom ekranu, izgleda kao oblačić, prekida funkciju Ne ometaj"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetno"</string> <string name="no_shortcut" msgid="8257177117568230126">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava funkcije razgovora"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ta obavještenja se ne mogu izmijeniti."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Nije moguće izmijeniti obavještenja o pozivima."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovu grupu obavještenja nije moguće konfigurirati ovdje"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Za višu rezoluciju obrnite telefon"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Sklopivi uređaj se rasklapa"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Sklopivi uređaj se obrće"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Prednji ekran je uključen"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sklopljeno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otklopljeno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite prečice na tastaturi"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pritisnite tipku da dodijelite prečicu"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ručica za prevlačenje"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tastature"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Postavi prečicu"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Otkaži"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Ta se kombinacija tipki već koristi. Pokušajte s drugom tipkom."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o prečicama tastature"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 0be3d80edcb7..b5396da8f80a 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de la pantalla de bloqueig"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entesos"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string> @@ -715,7 +719,7 @@ <string name="volume_panel_hint_muted" msgid="1124844870181285320">"silenciat"</string> <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibra"</string> <string name="media_output_label_title" msgid="872824698593182505">"S\'està reproduint <xliff:g id="LABEL">%s</xliff:g> a"</string> - <string name="media_output_title_without_playing" msgid="3825663683169305013">"Es reproduirà a"</string> + <string name="media_output_title_without_playing" msgid="3825663683169305013">"L\'àudio es reproduirà a"</string> <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Trucant des de"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"Personalitzador d\'interfície d\'usuari"</string> <string name="status_bar" msgid="4357390266055077437">"Barra d\'estat"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Es mostra a la part superior de les notificacions de les converses i com a foto de perfil a la pantalla de bloqueig, apareix com una bombolla, interromp el mode No molestis"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritat"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admet les funcions de converses"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Aquestes notificacions no es poden modificar."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Les notificacions de trucades no es poden modificar."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Aquest grup de notificacions no es pot configurar aquí"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Utilitza menys de <xliff:g id="LENGTH">%1$d</xliff:g> caràcters"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"copiar al porta-retalls."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa oberta"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Widgets de conversa"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Toca una conversa per afegir-la a la teva pantalla d\'inici"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Per a una resolució més alta, gira el telèfon"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositiu plegable desplegant-se"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositiu plegable girant"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"La pantalla frontal està activada"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalitza les tecles de drecera"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Prem la tecla per assignar la drecera"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hi ha cap resultat de la cerca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ansa per arrossegar"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuració del teclat"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Configura la drecera"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel·la"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Prem una tecla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"La combinació de tecles ja s\'està utilitzant. Prova-ho amb una altra tecla."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega amb el teclat"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprèn les tecles de drecera"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega amb el ratolí tàctil"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 1bbef7bce160..a6f0e6141801 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgety na obrazovce uzamčení"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"K otevření aplikace pomocí widgetu budete muset ověřit svou totožnost. Také mějte na paměti, že widgety uvidí kdokoli, i když tablet bude uzamčen. Některé widgety nemusí být pro obrazovku uzamčení určeny a nemusí být bezpečné je na ni přidat."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Rozumím"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Zobrazuje se v horní části sekce konverzací a na obrazovce uzamčení se objevuje jako profilová fotka, má podobu bubliny a deaktivuje režim Nerušit"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritní"</string> <string name="no_shortcut" msgid="8257177117568230126">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> funkce konverzace nepodporuje"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Tato oznámení nelze upravit."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Upozornění na hovor nelze upravit."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Tuto skupinu oznámení tady nelze nakonfigurovat"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Použijte méně než <xliff:g id="LENGTH">%1$d</xliff:g> znaků"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"zkopírovat do schránky"</string> <string name="basic_status" msgid="2315371112182658176">"Otevřít konverzaci"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Widgety konverzací"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Klepnutím na konverzaci ji přidáte na plochu"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Otočte telefon, abyste dosáhli vyššího rozlišení"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rozkládání rozkládacího zařízení"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Otáčení rozkládacího zařízení"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Přední obrazovka je zapnutá"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"složené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Přizpůsobení klávesových zkratek"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Nastavte zkratku stisknutím klávesy"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žádné výsledky hledání"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Úchyt pro přetažení"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavení klávesnice"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavit zkratku"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušit"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stiskněte klávesu"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinace kláves se už používá. Použijte jinou klávesu."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigujte pomocí klávesnice"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte se klávesové zkratky"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigujte pomocí touchpadu"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index b2e17a929900..f5d36d71deb2 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets på låseskærmen"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Hvis du vil åbne en app ved hjælp af en widget, skal du verificere din identitet. Husk også, at alle kan se dem, også når din tablet er låst. Nogle widgets er muligvis ikke beregnet til låseskærmen, og det kan være usikkert at tilføje dem her."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Vises øverst i samtalenotifikationer og som et profilbillede på låseskærmen. Vises som en boble, der afbryder Forstyr ikke"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> understøtter ikke samtalefunktioner"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Disse notifikationer kan ikke redigeres."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Opkaldsnotifikationer kan ikke redigeres."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Du kan ikke konfigurere denne gruppe notifikationer her"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Vend telefonen for at få højere opløsning"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldbar enhed foldes ud"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldbar enhed vendes om"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Frontskærmen er aktiveret"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"foldet"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"foldet ud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpas tastaturgenveje"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Tryk på en tast for at tildele genvejen"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Der er ingen søgeresultater"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtag"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturindstillinger"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Konfigurer genvej"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuller"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryk på en tast"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Tastekombinationen er allerede i brug. Prøv en anden tast."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger ved hjælp af dit tastatur"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Se tastaturgenveje"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger ved hjælp af din touchplade"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 5f7128e804d0..1573cb02e113 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sperrbildschirm-Widgets"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Wenn du eine App mit einem Widget öffnen möchtest, musst du deine Identität bestätigen. Beachte auch, dass jeder die Widgets sehen kann, auch wenn dein Tablet gesperrt ist. Einige Widgets sind möglicherweise nicht für den Sperrbildschirm vorgesehen, sodass es unsicher sein kann, sie hier hinzuzufügen."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Wird oben im Bereich „Unterhaltungen“ sowie als Profilbild auf dem Sperrbildschirm angezeigt, erscheint als Bubble, unterbricht „Bitte nicht stören“"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priorität"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> unterstützt keine Funktionen für Unterhaltungen"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Diese Benachrichtigungen können nicht geändert werden."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Anrufbenachrichtigungen können nicht geändert werden."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Die Benachrichtigungsgruppe kann hier nicht konfiguriert werden"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Für höhere Auflösung Smartphone umdrehen"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Faltbares Gerät wird geöffnet"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Faltbares Gerät wird umgeklappt"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Frontdisplay aktiviert"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zugeklappt"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aufgeklappt"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tastenkombinationen anpassen"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Drücke eine Taste, um eine Tastenkombination festzulegen"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Keine Suchergebnisse"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tastenkombination festlegen"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Abbrechen"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Taste drücken"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Diese Tastenkombination wird bereits verwendet. Versuche es mit einer anderen Taste."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigation mit der Tastatur"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informationen zu Tastenkombinationen"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigation mit dem Touchpad"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index fd8e37a34030..8da32147896c 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Γραφικά στοιχεία οθόνης κλειδώματος"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Εμφανίζεται στην κορυφή των ειδοποιήσεων συζήτησης και ως φωτογραφία προφίλ στην οθόνη κλειδώματος, εμφανίζεται ως συννεφάκι, διακόπτει τη λειτουργία Μην ενοχλείτε"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Προτεραιότητα"</string> <string name="no_shortcut" msgid="8257177117568230126">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν υποστηρίζει τις λειτουργίες συζήτησης"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Δεν είναι δυνατή η τροποποίηση αυτών των ειδοποιήσεων"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Δεν είναι δυνατή η τροποποίηση των ειδοποιήσεων κλήσεων."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Δεν είναι δυνατή η διαμόρφωση αυτής της ομάδας ειδοποιήσεων εδώ"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Χρησιμοποιήστε λιγότερους από <xliff:g id="LENGTH">%1$d</xliff:g> χαρακτήρες"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγράφηκε στο πρόχειρο."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"αντιγραφή στο πρόχειρο."</string> <string name="basic_status" msgid="2315371112182658176">"Άνοιγμα συνομιλίας"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Γραφικά στοιχεία συνομιλίας"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Πατήστε μια συνομιλία για να την προσθέσετε στην αρχική οθόνη"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Για υψηλότερη ανάλυση, αναστρέψτε το τηλέφωνο"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Αναδιπλούμενη συσκευή που ξεδιπλώνει"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Αναδιπλούμενη συσκευή που διπλώνει"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Η μπροστινή οθόνη ενεργοποιήθηκε"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"διπλωμένη"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ξεδιπλωμένη"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Προσβασιμότητα"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Προσαρμογή συντομεύσεων πληκτρολογίου"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Πατήστε το πλήκτρο για εκχώρηση της συντόμευσης"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Κανένα αποτέλεσμα αναζήτησης"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ρυθμίσεις πληκτρολογίου"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ορισμός συντόμευσης"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ακύρωση"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Πατήστε ένα πλήκτρο"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Ο συνδυασμός πλήκτρων χρησιμοποιείται ήδη. Δοκιμάστε άλλο πλήκτρο."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Πλοήγηση με το πληκτρολόγιο"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Μάθετε συντομεύσεις πληκτρολογίου"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Πλοήγηση με την επιφάνεια αφής"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 4280ff2ad994..e92783d6d1e5 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Use fewer than <xliff:g id="LENGTH">%1$d</xliff:g> characters"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"copy to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Conversation widgets"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Tap a conversation to add it to your home screen"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"For higher resolution, flip the phone"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Front screen turned on"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Press key to assign shortcut"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Key combination already in use. Try another key."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 3b009a1f1d14..b434c6e74a3b 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -528,6 +528,8 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string> + <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string> + <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add Widgets on the lock screen as a shortcut, make sure it is enabled in settings."</string> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> @@ -780,6 +782,7 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string> + <string name="notification_guts_bundle_feedback" msgid="5393570876655201459">"Provide Bundle Feedback"</string> <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string> @@ -1220,8 +1223,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Use fewer than <xliff:g id="LENGTH">%1$d</xliff:g> characters"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"copy to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Conversation widgets"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Tap a conversation to add it to your Home screen"</string> @@ -1417,7 +1419,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customize keyboard shortcuts"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Press key to assign shortcut"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> @@ -1430,9 +1437,13 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard Settings"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Key combination already in use. Try another key."</string> + <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string> + <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Shortcut cannot be set."</string> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 4280ff2ad994..e92783d6d1e5 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Use fewer than <xliff:g id="LENGTH">%1$d</xliff:g> characters"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"copy to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Conversation widgets"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Tap a conversation to add it to your home screen"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"For higher resolution, flip the phone"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Front screen turned on"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Press key to assign shortcut"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Key combination already in use. Try another key."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 4280ff2ad994..e92783d6d1e5 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> doesn’t support conversation features"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"These notifications can\'t be modified."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Call notifications can\'t be modified."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"This group of notifications cannot be configured here"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Use fewer than <xliff:g id="LENGTH">%1$d</xliff:g> characters"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"copy to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Conversation widgets"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Tap a conversation to add it to your home screen"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"For higher resolution, flip the phone"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Front screen turned on"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Press key to assign shortcut"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Key combination already in use. Try another key."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index ad403b1658ee..4ab2f2b56f8a 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets en la pantalla de bloqueo"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una app usando un widget, debes verificar tu identidad. Además, ten en cuenta que cualquier persona podrá verlo, incluso cuando la tablet esté bloqueada. Es posible que algunos widgets no se hayan diseñados para la pantalla de bloqueo y podría ser peligroso agregarlos allí."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparece en forma de burbuja y como foto de perfil en la parte superior de las notificaciones de conversación, en la pantalla de bloqueo, y detiene el modo No interrumpir"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaria"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite funciones de conversación"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"No se pueden modificar estas notificaciones."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"No se pueden modificar las notificaciones de llamada."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"No se puede configurar aquí este grupo de notificaciones"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Usa menos de <xliff:g id="LENGTH">%1$d</xliff:g> caracteres"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"copiar en el portapapeles."</string> <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Widgets de conversación"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Presiona una conversación para agregarla a tu pantalla principal"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personaliza las combinaciones de teclas"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Presiona la tecla para asignar el acceso directo"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"La búsqueda no arrojó resultados"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string> @@ -1431,9 +1441,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración del teclado"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Presiona una tecla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"La combinación de teclas ya está en uso. Prueba con otra."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega con el teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega con el panel táctil"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 50de6d2e452a..861d21e0f502 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets para la pantalla de bloqueo"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una aplicación usando un widget, deberás verificar que eres tú. Además, ten en cuenta que cualquier persona podrá verlos, incluso aunque tu tablet esté bloqueada. Es posible que algunos widgets no estén pensados para la pantalla de bloqueo y no sea seguro añadirlos aquí."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Se muestra encima de las notificaciones de conversaciones y como imagen de perfil en la pantalla de bloqueo, aparece como burbuja e interrumpe el modo No molestar"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite funciones de conversación"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Estas notificaciones no se pueden modificar."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Las notificaciones de llamada no se pueden modificar."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Este grupo de notificaciones no se puede configurar aquí"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Para una mayor resolución, gira el teléfono"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo plegable desplegándose"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo plegable mostrado desde varios ángulos"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Pantalla frontal encendida"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar las combinaciones de teclas"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pulsa una tecla para asignar una combinación de teclas"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hay resultados de búsqueda"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ajustes del teclado"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pulsa una tecla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"La combinación de teclas ya se está usando. Prueba con otra tecla."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Desplázate con el teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Desplázate con el panel táctil"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 9c0d56dad8c3..c2b140587b63 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukustuskuva vidinad"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Rakenduse avamiseks vidina abil peate kinnitama, et see olete teie. Samuti pidage meeles, et kõik saavad vidinaid vaadata, isegi kui teie tahvelarvuti on lukus. Mõni vidin ei pruugi olla ette nähtud teie lukustuskuva jaoks ja seda pole turvaline siia lisada."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selge"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Kuvatakse mullina vestluste märguannete ülaosas ja profiilipildina lukustuskuval ning katkestab režiimi Mitte segada"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteetne"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei toeta vestlusfunktsioone"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Neid märguandeid ei saa muuta."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Kõnemärguandeid ei saa muuta."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Seda märguannete rühma ei saa siin seadistada"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Kasutage vähem kui <xliff:g id="LENGTH">%1$d</xliff:g> tähemärki"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"lõikelauale kopeerimine."</string> <string name="basic_status" msgid="2315371112182658176">"Avage vestlus"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Vestlusvidinad"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Puudutage vestlust, et lisada see oma avakuvale"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Suurema eraldusvõime saavutamiseks pöörake telefon ümber"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Volditava seadme lahtivoltimine"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Volditava seadme ümberpööramine"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Esiekraan on sisse lülitatud"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kokku volditud"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"lahti volditud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Juurdepääsetavus"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatuuri otseteede kohandamine"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Otsetee lisamiseks vajutage klahvi"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsige otseteid"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Otsingutulemused puuduvad"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatuuri seaded"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Määrake otsetee"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Tühista"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Vajutage klahvi"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Klahvikombinatsioon juba kasutusel. Proovige mõnda muud klahvi."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeerige klaviatuuri abil"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Õppige klaviatuuri otseteid"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeerige puuteplaadi abil"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index a971d1c25a16..7b9a0ea69d29 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pantaila blokeatuko widgetak"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aplikazio bat widget baten bidez irekitzeko, zeu zarela egiaztatu beharko duzu. Gainera, kontuan izan edonork ikusi ahalko dituela halako widgetak, tableta blokeatuta badago ere. Baliteke widget batzuk pantaila blokeaturako egokiak ez izatea, eta agian ez da segurua haiek bertan gehitzea."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ados"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Elkarrizketen jakinarazpenen goialdean eta profileko argazki gisa agertzen da pantaila blokeatuan, burbuila batean, eta ez molestatzeko modua eteten du"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Lehentasuna"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez ditu onartzen elkarrizketetarako eginbideak"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Jakinarazpen horiek ezin dira aldatu."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Deien jakinarazpenak ezin dira aldatu."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Jakinarazpen talde hau ezin da konfiguratu hemen"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Irauli telefonoa bereizmen handiago a lortzeko"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Gailu tolesgarria zabaltzen"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Gailu tolesgarria biratzen"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Aurreko pantaila piztuta dago"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"tolestuta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tolestu gabe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pertsonalizatu lasterbideak"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Sakatu tekla lasterbidea esleitzeko"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ez dago bilaketa-emaitzarik"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Arrastatzeko kontrol-puntua"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Teklatuaren ezarpenak"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ezarri lasterbidea"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Utzi"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Sakatu tekla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Tekla-konbinazio hori erabili da dagoeneko. Probatu beste tekla bat."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nabigatu teklatua erabilita"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ikasi lasterbideak"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nabigatu ukipen-panela erabilita"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index bf7ccf0d8079..5401698fe3b6 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزارههای صفحه قفل"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را بهتأیید برسانید. همچنین، بهخاطر داشته باشید که همه میتوانند آنها را مشاهده کنند، حتی وقتی رایانه لوحیتان قفل است. برخیاز ابزارهها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آنها در اینجا ناامن باشد."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجهام"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایینپر"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"در بالای اعلانهای مکالمه و بهصورت عکس نمایه در صفحه قفل نشان داده میشود، بهصورت حبابک ظاهر میشود، در حالت «مزاحم نشوید» وقفه ایجاد میکند"</string> <string name="notification_priority_title" msgid="2079708866333537093">"اولویت"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> از ویژگیهای مکالمه پشتیبانی نمیکند"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"این اعلانها قابل اصلاح نیستند."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"این اعلانها قابلاصلاح نیستند."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"نمیتوانید این گروه اعلانها را در اینجا پیکربندی کنید"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"از کمتر از <xliff:g id="LENGTH">%1$d</xliff:g> نویسه استفاده کنید"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"شماره ساخت"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریدهدان کپی شد."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"کپی کردن در بریدهدان."</string> <string name="basic_status" msgid="2315371112182658176">"باز کردن مکالمه"</string> <string name="select_conversation_title" msgid="6716364118095089519">"ابزارکهای مکالمه"</string> <string name="select_conversation_text" msgid="3376048251434956013">"روی مکالمهای تکضرب بزنید تا به «صفحه اصلی» اضافه شود"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"برای وضوح بیشتر، تلفن را بچرخانید"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"دستگاه تاشو درحال باز شدن"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"دستگاه تاشو درحال چرخش به اطراف"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"صفحهنمایش جلو روشن شد"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"تاشده"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"تانشده"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"دسترسپذیری"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"میانبرهای صفحهکلید"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"سفارشیسازی کردن میانبرهای صفحهکلید"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"برای اختصاص دادن میانبر، کلید را فشار دهید"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میانبرها"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"نتیجهای برای جستجو پیدا نشد"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"تنظیمات صفحهکلید"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"تنظیم میانبر"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"لغو"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید را فشار دهید"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"ترکیب کلید ازقبل درحال استفاده است. کلید دیگری را امتحان کنید."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"پیمایش کردن بااستفاده از صفحهکلید"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"آشنایی با میانبرهای صفحهکلید"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"پیمایش کردن بااستفاده از صفحه لمسی"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 89ed95f0115f..58b86faaa32f 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukitusnäytön widgetit"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Jos haluat avata sovelluksen käyttämällä widgetiä, sinun täytyy vahvistaa henkilöllisyytesi. Muista myös, että widgetit näkyvät kaikille, vaikka tabletti olisi lukittuna. Jotkin widgetit on ehkä tarkoitettu lukitusnäytölle, ja niiden lisääminen tänne ei välttämättä ole turvallista."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selvä"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Näkyy keskusteluilmoitusten yläosassa ja profiilikuvana lukitusnäytöllä, näkyy kuplana, keskeyttää Älä häiritse ‑tilan"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Tärkeä"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei tue keskusteluominaisuuksia"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Näitä ilmoituksia ei voi muokata"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Puheluilmoituksia ei voi muokata."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Tätä ilmoitusryhmää ei voi määrittää tässä"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Resoluutio on parempi, kun käännät puhelimen"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Taitettava laite taitetaan"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Taitettava laite käännetään ympäri"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Etunäyttö päällä"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"taitettu"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"taittamaton"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pikanäppäimien muokkaaminen"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Määritä pikanäppäin painamalla näppäintä"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ei hakutuloksia"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vetokahva"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Näppäimistön asetukset"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Valitse pikanäppäin"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Peru"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paina näppäintä"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Näppäinyhdistelmä on jo käytössä. Kokeile toista näppäintä."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Siirry käyttämällä näppäimistöä"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Opettele pikanäppäimiä"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Siirry käyttämällä kosketuslevyä"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 708d8b910306..ff3da656020a 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de l\'écran de verrouillage"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut voir les widgets, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage, et il pourrait être dangereux de les ajouter ici."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applis et les données de cette session seront supprimées."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"S\'affiche dans le haut des notifications de conversation et comme photo de profil à l\'écran de verrouillage, s\'affiche comme bulle, interrompt le mode Ne pas déranger"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaire"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne prend pas en charge les fonctionnalités de conversation"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ces notifications ne peuvent pas être modifiées"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Les notifications d\'appel ne peuvent pas être modifiées."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ce groupe de notifications ne peut pas être configuré ici"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Pour une meilleure résolution, retournez le téléphone"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable en cours de dépliage"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable en train d\'être retourné"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Écran avant activé"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis-clavier"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Appuyez sur la touche pour attribuer un raccourci"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Combinaison de touches déjà utilisée. Essayez une autre touche."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide de votre clavier"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis-clavier"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 41bd9106fb45..e51cb07869a7 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets pour l\'écran de verrouillage"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devez confirmer qu\'il s\'agit bien de vous. N\'oubliez pas non plus que tout le monde peut voir vos widgets, même lorsque votre tablette est verrouillée. Certains d\'entre eux n\'ont pas été conçus pour l\'écran de verrouillage et les ajouter à cet endroit peut s\'avérer dangereux."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"S\'affiche en haut des notifications de conversation et en tant que photo de profil sur l\'écran de verrouillage, apparaît sous forme de bulle, interrompt le mode Ne pas déranger"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaire"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas compatible avec les fonctionnalités de conversation"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Impossible de modifier ces notifications."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Impossible de modifier les notifications d\'appel."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Vous ne pouvez pas configurer ce groupe de notifications ici"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Pour une résolution plus élevée, retournez le téléphone"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable qui est déplié"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable qui est retourné"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Écran avant activé"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis clavier"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Appuyez sur une touche pour attribuer un raccourci"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string> @@ -1431,15 +1441,22 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Combinaison de touches déjà utilisée. Essayez une autre touche."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide du clavier"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis clavier"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Découvrez les gestes au pavé tactile"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Découvrir les gestes au pavé tactile, les raccourcis clavier et plus encore"</string> + <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Découvrir les gestes du pavé tactile, les raccourcis clavier et plus encore"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string> <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à l\'accueil"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 398360689afe..6a1aced1ebf6 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da pantalla de bloqueo"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir unha aplicación mediante un widget, tes que verificar a túa identidade. Ten en conta que pode velos calquera persoa, mesmo coa tableta bloqueada. Pode ser que algúns widgets non estean pensados para a túa pantalla de bloqueo, polo que talvez non sexa seguro engadilos aquí."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Móstrase na parte superior das notificacións das conversas e como imaxe do perfil na pantalla de bloqueo, aparece como unha burbulla e interrompe o modo Non molestar"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non admite funcións de conversa"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Estas notificacións non se poden modificar."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"As notificacións de chamadas non se poden modificar."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Aquí non se pode configurar este grupo de notificacións"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Dálle a volta ao teléfono para gozar dunha maior resolución"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pregable abríndose"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pregable xirando"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Activouse a pantalla dianteira"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dispositivo pregado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dispositivo despregado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidade"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar os atallos de teclado"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Preme a tecla para asignar o atallo"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Busca atallos"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Non hai resultados de busca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración do teclado"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atallo"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Preme unha tecla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Xa se está usando esta combinación de teclas. Proba con outra."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega co teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende a usar os atallos de teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega co panel táctil"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 89012697cfac..566ccd0600f5 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"લૉક સ્ક્રીન વિજેટ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"વિજેટનો ઉપયોગ કરીને ઍપ ખોલવા માટે, તમારે એ ચકાસણી કરવાની જરૂર રહેશે કે આ તમે જ છો. તે ઉપરાંત, ધ્યાનમાં રાખો કે તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ વ્યક્તિ તેમને જોઈ શકે છે. અમુક વિજેટ કદાચ તમારી લૉક સ્ક્રીન માટે બનાવવામાં આવ્યા ન હોઈ શકે છે અને તેમને અહીં ઉમેરવાનું અસલામત હોઈ શકે છે."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"સમજાઈ ગયું"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string> @@ -672,9 +676,9 @@ <string name="screen_pinning_negative" msgid="6882816864569211666">"ના, આભાર"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"ઍપ પિન કરી"</string> <string name="screen_pinning_exit" msgid="4553787518387346893">"ઍપ અનપિન કરી"</string> - <string name="stream_voice_call" msgid="7468348170702375660">"કૉલ કરો"</string> + <string name="stream_voice_call" msgid="7468348170702375660">"કૉલ"</string> <string name="stream_system" msgid="7663148785370565134">"સિસ્ટમ"</string> - <string name="stream_ring" msgid="7550670036738697526">"રિંગ વગાડો"</string> + <string name="stream_ring" msgid="7550670036738697526">"રિંગ"</string> <string name="stream_music" msgid="2188224742361847580">"મીડિયા"</string> <string name="stream_alarm" msgid="16058075093011694">"અલાર્મ"</string> <string name="stream_notification" msgid="7930294049046243939">"નોટિફિકેશન"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string> <string name="notification_priority_title" msgid="2079708866333537093">"પ્રાધાન્યતા"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> વાતચીતની સુવિધાઓને સપોર્ટ આપતી નથી"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"આ નોટિફિકેશનમાં કોઈ ફેરફાર થઈ શકશે નહીં."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"કૉલના નોટિફિકેશનમાં કોઈ ફેરફાર કરી શકાતો નથી."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"નોટિફિકેશનના આ ગ્રૂપની ગોઠવણી અહીં કરી શકાશે નહીં"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"વધુ રિઝોલ્યુશન માટે, ફોનને ફ્લિપ કરો"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ફોલ્ડ કરી શકાય એવું ડિવાઇસ અનફોલ્ડ કરવામાં આવી રહ્યું છે"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ફોલ્ડ કરી શકાય એવું ડિવાઇસ ફ્લિપ કરવામાં આવી રહ્યું છે"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ફ્રન્ટ સ્ક્રીનની સુવિધા ચાલુ કરેલી છે"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ફોલ્ડ કરેલું"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"અનફોલ્ડ કરેલું"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ઍક્સેસિબિલિટી"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"કીબોર્ડ શૉર્ટકટ"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"કીબોર્ડ શૉર્ટકટને કસ્ટમાઇઝ કરો"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"શૉર્ટકટ સોંપવા માટે દી દબાવો"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"કોઈ શોધ પરિણામો નથી"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"કીબોર્ડના સેટિંગ"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"શૉર્ટકટ સેટ કરો"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"રદ કરો"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"કી દબાવો"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"કી સંયોજન પેહલેથી ઉપયોગમાં છે. અન્ય કી અજમાવી જુઓ."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"તમારા કીબોર્ડ વડે નૅવિગેટ કરો"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"કીબોર્ડ શૉર્ટકર્ટ જાણો"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"તમારા ટચપૅડ વડે નૅવિગેટ કરો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 241e2e630013..255fdc5b571d 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"यह कई तरीकों से दिखती है, जैसे कि बातचीत वाली सूचनाओं में सबसे ऊपर, बबल के तौर पर, और लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो के तौर पर. साथ ही, यह \'परेशान न करें\' मोड को बायपास कर सकती है"</string> <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर बातचीत की सुविधाएं काम नहीं करतीं"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ये सूचनाएं नहीं बदली जा सकती हैं."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कॉल से जुड़ी सूचनाओं को ब्लॉक नहीं किया जा सकता."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"सूचनाओं के इस समूह को यहां कॉन्फ़िगर नहीं किया जा सकता"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"<xliff:g id="LENGTH">%1$d</xliff:g> वर्ण से कम इस्तेमाल करें"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"क्लिपबोर्ड पर कॉपी करें."</string> <string name="basic_status" msgid="2315371112182658176">"ऐसी बातचीत जिसमें इंटरैक्शन डेटा मौजूद नहीं है"</string> <string name="select_conversation_title" msgid="6716364118095089519">"बातचीत वाला विजेट"</string> <string name="select_conversation_text" msgid="3376048251434956013">"किसी बातचीत को होम स्क्रीन पर जोड़ने के लिए, उस बातचीत पर टैप करें"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"बेहतर रिज़ॉल्यूशन वाली फ़ोटो खींचने के लिए, फ़ोन को फ़्लिप करें"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फ़ोल्ड किया जा सकने वाला डिवाइस अनफ़ोल्ड किया जा रहा है"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फ़ोल्ड किया जा सकने वाला डिवाइस पलटा जा रहा है"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"फ़्रंट स्क्रीन चालू है"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"डिवाइस फ़ोल्ड किया गया"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"डिवाइस अनफ़ोल्ड किया गया"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सुलभता"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट को पसंद के मुताबिक बनाएं"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"शॉर्टकट असाइन करने के लिए बटन दबाएं"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शॉर्टकट खोजें"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"खोज का कोई नतीजा नहीं मिला"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"शॉर्टकट सेट करें"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करें"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"बटन दबाएं"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"बटन का यह कॉम्बिनेशन पहले से इस्तेमाल किया जा रहा है. कोई दूसरा कॉम्बिनेशन आज़माएं."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"कीबोर्ड का इस्तेमाल करके नेविगेट करें"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट के बारे में जानें"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचपैड का इस्तेमाल करके नेविगेट करें"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index a179649922d2..ef5cf7316bda 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeti na zaključanom zaslonu"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju pomoću widgeta, trebate potvrditi da ste to vi. Također napominjemo da ih svatko može vidjeti, čak i ako je vaš tablet zaključan. Neki widgeti možda nisu namijenjeni za zaključani zaslon, pa ih možda nije sigurno dodati ovdje."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Shvaćam"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikazuje se pri vrhu obavijesti razgovora i kao profilna slika na zaključanom zaslonu, izgleda kao oblačić, prekida Ne uznemiravaj"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetno"</string> <string name="no_shortcut" msgid="8257177117568230126">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava značajke razgovora"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Te se obavijesti ne mogu izmijeniti."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Obavijesti o pozivima ne mogu se izmijeniti."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ta se grupa obavijesti ne može konfigurirati ovdje"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Za višu razlučivost okrenite telefon"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rasklopljen sklopivi uređaj"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Okretanje sklopivog uređaja sa svih strana"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Prednji zaslon je uključen"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodba tipkovnih prečaca"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pritisnite tipku da biste dodijelili prečac"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za povlačenje"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tipkovnice"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Postavite prečac"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Odustani"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinacija tipki već se upotrebljava. Pokušajte s drugom tipkom."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tipkovnice"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tipkovnim prečacima"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 4b5b9162b01b..350dc4fcb8ca 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"A lezárási képernyő moduljai"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ha modul használatával szeretne megnyitni egy alkalmazást, igazolnia kell a személyazonosságát. Ne felejtse továbbá, hogy bárki megtekintheti a modulokat, még akkor is, amikor zárolva van a táblagép. Előfordulhat, hogy bizonyos modulokat nem a lezárási képernyőn való használatra terveztek, ezért nem biztonságos a hozzáadásuk."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Értem"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"A beszélgetésekre vonatkozó értesítések tetején, lebegő buborékként látható, megjeleníti a profilképet a lezárási képernyőn, és megszakítja a Ne zavarjanak funkciót"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritás"</string> <string name="no_shortcut" msgid="8257177117568230126">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> nem támogatja a beszélgetési funkciókat"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ezeket az értesítéseket nem lehet módosítani."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"A hívásértesítéseket nem lehet módosítani."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Az értesítések jelen csoportját itt nem lehet beállítani"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"A nagyobb felbontás érdekében fordítsa meg a telefont"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Összehajtható eszköz kihajtása"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Összehajtható eszköz körbeforgatása"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Előlapi képernyő bekapcsolva"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"összehajtva"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kihajtva"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"A billentyűparancsok személyre szabása"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Nyomja meg a billentyűt a parancsikon hozzárendeléséhez"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nincsenek keresési találatok"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Billentyűzetbeállítások"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Billentyűparancs beállítása"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Mégse"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nyomja le a billentyűt"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"A billentyűkombináció már használatban van. Próbálkozzon másik kulccsal."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigáció a billentyűzet segítségével"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Billentyűparancsok megismerése"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigálás az érintőpaddal"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index a06209867663..9c833011ee21 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Կողպէկրանի վիջեթներ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Ցուցադրվում է զրույցների ծանուցումների վերևում, ինչպես նաև կողպէկրանին որպես պրոֆիլի նկար, հայտնվում է ամպիկի տեսքով, ընդհատում է «Չանհանգստացնել» ռեժիմը"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Կարևոր"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը զրույցի գործառույթներ չի աջակցում"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Այս ծանուցումները չեն կարող փոփոխվել:"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Զանգերի մասին ծանուցումները հնարավոր չէ փոփոխել։"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ծանուցումների տվյալ խումբը հնարավոր չէ կարգավորել այստեղ"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Օգտագործեք մինչև <xliff:g id="LENGTH">%1$d</xliff:g> նիշ"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Կառուցման համարը"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Կառուցման համարը պատճենվեց սեղմատախտակին։"</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"պատճենել սեղմատախտակին։"</string> <string name="basic_status" msgid="2315371112182658176">"Բաց զրույց"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Զրույցի վիջեթներ"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Հպեք զրույցին՝ այն հիմնական էկրանին ավելացնելու համար"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Ավելի մեծ լուծաչափի համար շրջեք հեռախոսը"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ծալովի սարք՝ բացված վիճակում"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Ծալովի սարք՝ շրջված վիճակում"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Առջևի էկրանը միացված է"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ծալված"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"բացված"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Հատուկ գործառույթներ"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Կարգավորեք ստեղնային դյուրանցումներ"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Սեղմեք որևէ ստեղն՝ դյուրանցում նշանակելու համար"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Որոնման արդյունքներ չկան"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ստեղծել դյուրանցում"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Չեղարկել"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Սեղմեք որևէ ստեղն"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Ստեղների համակցությունն արդեն օգտագործվում է։ Ընտրեք այլ ստեղն։"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Կողմնորոշվեք ձեր ստեղնաշարի օգնությամբ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Սովորեք օգտագործել ստեղնային դյուրանցումները"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Կողմնորոշվեք ձեր հպահարթակի օգնությամբ"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index cf9b093a1d43..58009872e1ae 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget layar kunci"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka aplikasi menggunakan widget, Anda perlu memverifikasi diri Anda. Selain itu, harap ingat bahwa siapa saja dapat melihatnya, bahkan saat tablet Anda terkunci. Beberapa widget mungkin tidak dirancang untuk layar kunci Anda dan mungkin tidak aman untuk ditambahkan di sini."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Oke"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Muncul teratas di notifikasi percakapan dan sebagai foto profil di layar kunci, ditampilkan sebagai balon, menimpa mode Jangan Ganggu"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritas"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak mendukung fitur percakapan"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Notifikasi ini tidak dapat diubah."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Notifikasi panggilan tidak dapat diubah."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Grup notifikasi ini tidak dapat dikonfigurasi di sini"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Untuk resolusi lebih tinggi, balik ponsel"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Perangkat foldable sedang dibentangkan"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Perangkat foldable sedang dibalik"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Layar depan diaktifkan"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ditutup"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dibuka"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Menyesuaikan pintasan keyboard"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Tekan tombol untuk menetapkan pintasan"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Telusuri pintasan"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tidak ada hasil penelusuran"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handel geser"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setelan Keyboard"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setel pintasan"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan tombol"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinasi tombol sudah digunakan. Coba tombol lain."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Menggunakan keyboard untuk navigasi"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Pelajari pintasan keyboard"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Menavigasi menggunakan touchpad"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index fe32aba3d75b..3a3659013f84 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Græjur fyrir lásskjá"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Þú þarft að staðfesta að þetta sért þú til að geta opnað forrit með græju. Hafðu einnig í huga að hver sem er getur skoðað þær, jafnvel þótt spjaldtölvan sé læst. Sumar græjur eru hugsanlega ekki ætlaðar fyrir lásskjá og því gæti verið óöruggt að bæta þeim við hér."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ég skil"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Birtist efst í samtalstilkynningum og sem prófílmynd á lásskjánum. Birtist sem blaðra sem truflar „Ónáðið ekki“"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Forgangur"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> styður ekki samtalseiginleika"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ekki er hægt að breyta þessum tilkynningum."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Ekki er hægt að breyta tilkynningum um símtöl."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ekki er hægt að stilla þessar tilkynningar hér"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Snúðu símanum til að fá betri upplausn"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Samanbrjótanlegt tæki opnað"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Samanbrjótanlegu tæki snúið við"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Kveikt á fremri skjá"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"samanbrotið"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opið"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sérsníddu flýtilykla"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Ýttu á lykil til að stilla flýtileið"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leita að flýtileiðum"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Engar leitarniðurstöður"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dragkló"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Stillingar lyklaborðs"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stilltu flýtileið"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Hætta við"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Ýttu á lykil"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Lyklasamsetning er þegar í notkun. Prófaðu annan lykil."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Flettu með því að nota lyklaborðið"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Kynntu þér flýtilykla"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Flettu með því að nota snertiflötinn"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index a66ea91eb396..12a01ebd003d 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -167,7 +167,7 @@ <string name="issuerecord_start_error" msgid="3402782952722871190">"Impossibile avviare la registrazione del problema"</string> <string name="immersive_cling_title" msgid="8372056499315585941">"Visualizzazione a schermo intero"</string> <string name="immersive_cling_description" msgid="2717426731830851921">"Per uscire, scorri verso il basso dalla parte superiore dello schermo"</string> - <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string> + <string name="immersive_cling_positive" msgid="3076681691468978568">"Ok"</string> <string name="accessibility_back" msgid="6530104400086152611">"Indietro"</string> <string name="accessibility_home" msgid="5430449841237966217">"Home"</string> <string name="accessibility_menu" msgid="2701163794470513040">"Menu"</string> @@ -528,6 +528,8 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget della schermata di blocco"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per aprire un\'app utilizzando un widget, dovrai verificare la tua identità. Inoltre tieni presente che chiunque può vederlo, anche quando il tablet è bloccato. Alcuni widget potrebbero non essere stati progettati per la schermata di blocco e potrebbe non essere sicuro aggiungerli qui."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string> + <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string> + <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Per aggiungere Widget alla schermata di blocco come scorciatoia, assicurati che sia abilitata nelle impostazioni."</string> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string> @@ -668,7 +670,7 @@ <string name="screen_pinning_toast" msgid="8177286912533744328">"Per sbloccare questa app, tocca e tieni premuti i pulsanti Indietro e Panoramica"</string> <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Per sbloccare questa app, tocca e tieni premuti i pulsanti Indietro e Home"</string> <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Per sbloccare questa app, scorri verso l\'alto e tieni premuto"</string> - <string name="screen_pinning_positive" msgid="3285785989665266984">"OK"</string> + <string name="screen_pinning_positive" msgid="3285785989665266984">"Ok"</string> <string name="screen_pinning_negative" msgid="6882816864569211666">"No, grazie"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"App bloccata"</string> <string name="screen_pinning_exit" msgid="4553787518387346893">"App sbloccata"</string> @@ -751,7 +753,7 @@ <string name="tuner_warning_title" msgid="7721976098452135267">"Il divertimento riservato a pochi eletti"</string> <string name="tuner_warning" msgid="1861736288458481650">"L\'Ottimizzatore UI di sistema mette a disposizione altri metodi per modificare e personalizzare l\'interfaccia utente di Android. Queste funzioni sperimentali potrebbero cambiare, interrompersi o scomparire nelle versioni successive. Procedi con cautela."</string> <string name="tuner_persistent_warning" msgid="230466285569307806">"Queste funzioni sperimentali potrebbero cambiare, interrompersi o scomparire nelle versioni successive. Procedi con cautela."</string> - <string name="got_it" msgid="477119182261892069">"OK"</string> + <string name="got_it" msgid="477119182261892069">"Ok"</string> <string name="tuner_toast" msgid="3812684836514766951">"Complimenti! L\'Ottimizzatore UI di sistema è stato aggiunto alle impostazioni."</string> <string name="remove_from_settings" msgid="633775561782209994">"Rimuovi dalle impostazioni"</string> <string name="remove_from_settings_prompt" msgid="551565437265615426">"Vuoi rimuovere l\'Ottimizzatore UI di sistema dalle impostazioni e smettere di utilizzare tutte le sue funzioni?"</string> @@ -780,6 +782,7 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Appare in cima alle notifiche delle conversazioni, come immagine del profilo nella schermata di blocco e sotto forma di bolla, inoltre interrompe la modalità Non disturbare"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priorità"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non supporta le funzionalità delle conversazioni"</string> + <string name="notification_guts_bundle_feedback" msgid="5393570876655201459">"Fornisci feedback sul bundle"</string> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Impossibile modificare queste notifiche."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Impossibile modificare gli avvisi di chiamata."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Qui non è possibile configurare questo gruppo di notifiche"</string> @@ -1357,8 +1360,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Gira il telefono per una maggiore risoluzione"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pieghevole che viene aperto"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pieghevole che viene capovolto"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Schermo frontale attivato"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"Piegato"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"Non piegato"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1420,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilità"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizza scorciatoie da tastiera"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Premi un tasto per assegnare una scorciatoia"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nessun risultato di ricerca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string> @@ -1431,15 +1438,20 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Punto di trascinamento"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Impostazioni tastiera"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Imposta scorciatoia"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annulla"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Premi un tasto"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Combinazione di tasti già in uso. Prova con un altro tasto."</string> + <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinazione di tasti già in uso. Prova con un altro tasto."</string> + <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Impossibile impostare la scorciatoia."</string> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviga usando la tastiera"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informazioni sulle scorciatoie da tastiera"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviga usando il touchpad"</string> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Impara i gesti con il touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviga usando la tastiera e il touchpad"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Scopri gesti con il touchpad, scorciatoie da tastiera e altro ancora"</string> + <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Impara i gesti con il touchpad, le scorciatoie da tastiera e altro ancora"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Indietro"</string> <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Vai alla schermata Home"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Visualizza app recenti"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 7b4245ee1c1e..0a14717645e6 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -310,7 +310,7 @@ <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string> - <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"הפעלה אוטומטית מחר"</string> + <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"הפעלה אוטומטית ביום הבא"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"תכונות כמו \'שיתוף מהיר\' ו\'איפה המכשיר שלי\' משתמשות ב-Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"חיבור ה-Bluetooth יופעל מחר בבוקר"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"שיתוף האודיו"</string> @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ווידג\'טים במסך הנעילה"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string> @@ -699,7 +703,7 @@ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"אודיו מרחבי"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"השבתה"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב סטטי"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב אחר תנועות הראש"</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב ראש"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string> <string name="volume_ringer_mode" msgid="6867838048430807128">"מצב תוכנת הצלצול"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"מוצגת בחלק העליון של קטע התראות השיחה וכתמונת פרופיל במסך הנעילה, מופיעה בבועה צפה ומפריעה במצב \'נא לא להפריע\'"</string> <string name="notification_priority_title" msgid="2079708866333537093">"בעדיפות גבוהה"</string> <string name="no_shortcut" msgid="8257177117568230126">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא תומכת בתכונות השיחה"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"לא ניתן לשנות את ההתראות האלה."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"לא ניתן לשנות את התראות השיחה."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"לא ניתן להגדיר כאן את קבוצת ההתראות הזו"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"כדי לצלם תמונה ברזולוציה גבוהה יותר, כדאי להפוך את הטלפון"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"מכשיר מתקפל עובר למצב לא מקופל"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"מכשיר מתקפל עובר למצב מהופך"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"המסך הקדמי מופעל"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"מצב מקופל"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"מצב לא מקופל"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"נגישות"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"התאמה אישית של מקשי הקיצור"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"יש ללחוץ על מקש כדי להקצות מקש קיצור"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"אין תוצאות חיפוש"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string> @@ -1431,15 +1441,22 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"הגדרות המקלדת"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"הגדרה של מקש קיצור"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ביטול"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"יש ללחוץ על מקש"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"שילוב המקשים הזה כבר בשימוש. אפשר לנסות מקש אחר."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ניווט באמצעות המקלדת"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"מידע על מקשי קיצור"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ניווט באמצעות לוח המגע"</string> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"מידע על התנועות בלוח המגע"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ניווט באמצעות המקלדת ולוח המגע"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"מידע על התנועות בלוח המגע, מקשי קיצור ועוד"</string> + <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ניווט עם המקלדת ולוח המגע"</string> + <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"כאן אפשר לקרוא איך מפעילים תנועות בלוח המגע, מקשי קיצור ועוד"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"חזרה"</string> <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"חזרה לדף הבית"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"הצגת האפליקציות האחרונות"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index c4fd62b355f6..1c106cfc4b0b 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ロック画面ウィジェット"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ウィジェットを使用してアプリを起動するには、本人確認が必要です。タブレットがロックされた状態でも他のユーザーにウィジェットが表示されますので、注意してください。一部のウィジェットについてはロック画面での使用を想定していないため、ロック画面への追加は危険な場合があります。"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"会話通知の一番上に表示されると同時に、ロック画面にプロフィール写真として表示されるほか、バブルとして表示され、サイレント モードが中断されます"</string> <string name="notification_priority_title" msgid="2079708866333537093">"優先"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>は会話機能に対応していません"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"これらの通知は変更できません。"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"着信通知は変更できません。"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"このグループの通知はここでは設定できません"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"使用できる文字数は <xliff:g id="LENGTH">%1$d</xliff:g> 文字未満です"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ビルド番号"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ビルド番号をクリップボードにコピーしました。"</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"クリップボードにコピー。"</string> <string name="basic_status" msgid="2315371112182658176">"空の会話"</string> <string name="select_conversation_title" msgid="6716364118095089519">"会話ウィジェット"</string> <string name="select_conversation_text" msgid="3376048251434956013">"会話をタップするとホーム画面に追加されます"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"高解像度で撮るにはスマートフォンを裏返してください"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"折りたたみ式デバイスが広げられている"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"折りたたみ式デバイスがひっくり返されている"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"フロント画面が ON になりました"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折りたたんだ状態"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"広げた状態"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ユーザー補助"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"キーボード ショートカット"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"キーボード ショートカットをカスタマイズする"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ショートカットを割り当てるキーを押してください"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"検索結果がありません"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ドラッグ ハンドル"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"キーボードの設定"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ショートカットの設定"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"キャンセル"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"キーを押してください"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"このキーの組み合わせはすでに使用されています。別のキーを試してください。"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"キーボードを使用して移動する"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"キーボード ショートカットの詳細"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"タッチパッドを使用して移動する"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 7cb3c8a99303..962a62c25cf6 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"დაბლოკილი ეკრანის ვიჯეტები"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"უნდა დაადასტუროთ თქვენი ვინაობა, რათა გახსნათ აპი ვიჯეტის გამოყენებით. გაითვალისწინეთ, რომ ნებისმიერს შეუძლია მათი ნახვა, მაშინაც კი, როცა ტაბლეტი დაბლოკილია. ზოგი ვიჯეტი შეიძლება არ იყოს გათვლილი თქვენი დაბლოკილი ეკრანისთვის და მათი აქ დამატება შეიძლება სახიფათო იყოს."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"გასაგებია"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"გამოჩნდება საუბრის შეტყობინებების თავში და პროფილის სურათის სახით ჩაკეტილ ეკრანზე, ჩნდება ბუშტის სახით, წყვეტს ფუნქციას „არ შემაწუხოთ“"</string> <string name="notification_priority_title" msgid="2079708866333537093">"პრიორიტეტი"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ს არ აქვს მიმოწერის ფუნქციების მხარდაჭერა"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ამ შეტყობინებების შეცვლა შეუძლებელია."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ზარის შეტყობინებების შეცვლა შეუძლებელია."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"შეტყობინებების ამ ჯგუფის კონფიგურირება აქ შეუძლებელია"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"მაღალი გარჩევადობისთვის ამოაბრუნეთ ტელეფონი"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"დასაკეცი მოწყობილობა იხსნება"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"დასაკეცი მოწყობილობა ტრიალებს"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"წინა ეკრანი ჩართულია"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"დაკეცილი"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"გაშლილი"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"მისაწვდომობა"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"კლავიატურის მალსახმობები"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"კლავიატურის მალსახმობების მორგება"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"მალსახმობის მინიჭებისთვის დააჭირეთ კლავიშს"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ძიების შედეგები არ არის"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"სახელური ჩავლებისთვის"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"კლავიატურის პარამეტრები"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"მალსახმობის დაყენება"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"გაუქმება"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"დააჭირეთ კლავიშს"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"კლავიშების კომბინაცია უკვე გამოიყენება. ცადეთ სხვა კლავიში."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ნავიგაცია კლავიატურის გამოყენებით"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"კლავიატურის მალსახმობების სწავლა"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ნავიგაცია სენსორული პანელის გამოყენებით"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 4122eeef9d59..ec1f09693eec 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Құлып экранының виджеттері"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Қолданбаны виджет көмегімен ашу үшін жеке басыңызды растауыңыз керек. Сондай-ақ басқалар оларды планшетіңіз құлыптаулы кезде де көре алатынын ескеріңіз. Кейбір виджеттер құлып экранына арналмаған болады, сондықтан оларды мұнда қосу қауіпсіз болмауы мүмкін."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түсінікті"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Әңгіме туралы хабарландырулардың жоғарғы жағында тұрады және құлыптаулы экранда профиль суреті болып көрсетіледі, қалқыма хабар түрінде шығады, Мазаламау режимін тоқтатады."</string> <string name="notification_priority_title" msgid="2079708866333537093">"Маңызды"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> әңгіме функцияларын қолдамайды."</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Бұл хабарландыруларды өзгерту мүмкін емес."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Қоңырау туралы хабарландыруларды өзгерту мүмкін емес."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Мұндай хабарландырулар бұл жерде конфигурацияланбайды."</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Жоғары ажыратымдылық үшін телефонды айналдырыңыз."</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Бүктемелі құрылғы ашылып жатыр."</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Бүктемелі құрылғы аударылып жатыр."</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Алдыңғы экран қосылды."</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"жабық"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ашық"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Арнайы мүмкіндіктер"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Пернелер тіркесімін бейімдеу"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Жылдам пәрменді тағайындау үшін пернені басыңыз."</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Іздеу нәтижелері жоқ."</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Пернетақта параметрлері"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Жылдам пәрменді орнату"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Бас тарту"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Пернені басыңыз"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Бұл пернелер тіркесімі қазір қолданыста. Басқа пернені таңдаңыз."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Пернетақтамен жұмыс істеңіз"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Перне тіркесімдерін үйреніңіз."</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Сенсорлық тақтамен жұмыс істеңіз"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index f9ec58f2fba8..2533f98132a5 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ធាតុក្រាហ្វិកលើអេក្រង់ចាក់សោ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ដើម្បីបើកកម្មវិធីដោយប្រើធាតុក្រាហ្វិក អ្នកនឹងត្រូវផ្ទៀងផ្ទាត់ថាជាអ្នក។ ទន្ទឹមនឹងនេះ សូមចងចាំថា នរណាក៏អាចមើលធាតុក្រាហ្វិកបាន សូម្បីពេលថេប្លេតរបស់អ្នកជាប់សោក៏ដោយ។ ធាតុក្រាហ្វិកមួយចំនួនប្រហែលមិនត្រូវបានរចនាឡើងសម្រាប់អេក្រង់ចាក់សោរបស់អ្នកទេ និងមិនមានសុវត្ថិភាពឡើយ បើបញ្ចូលទៅទីនេះ។"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"យល់ហើយ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរអ្នកប្រើ"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយទាញចុះ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យទាំងអស់ក្នុងវគ្គនេះនឹងត្រូវលុប។"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"បង្ហាញនៅខាងលើការជូនដំណឹងអំពីការសន្ទនា និងជារូបភាពកម្រងព័ត៌មាននៅលើអេក្រង់ចាក់សោ បង្ហាញជាពពុះ បង្អាក់មុខងារកុំរំខាន"</string> <string name="notification_priority_title" msgid="2079708866333537093">"អាទិភាព"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> មិនអាចប្រើមុខងារសន្ទនាបានទេ"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"មិនអាចកែប្រែការជូនដំណឹងទាំងនេះបានទេ។"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"មិនអាចកែប្រែការជូនដំណឹងអំពីការហៅទូរសព្ទបានទេ។"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"មិនអាចកំណត់រចនាសម្ព័ន្ធក្រុមការជូនដំណឹងនេះនៅទីនេះបានទេ"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"សម្រាប់កម្រិតគុណភាពកាន់តែខ្ពស់ សូមត្រឡប់ទូរសព្ទ"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ឧបករណ៍អាចបត់បានកំពុងត្រូវបានលា"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ឧបករណ៍អាចបត់បានកំពុងត្រូវបានលា"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"បានបើកអេក្រង់ខាងមុខ"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"បត់"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"លា"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ភាពងាយស្រួល"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"ផ្លូវកាត់ក្ដារចុច"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ប្ដូរផ្លូវកាត់ក្ដារចុចតាមបំណង"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ចុចគ្រាប់ចុច ដើម្បីកំណត់ផ្លូវកាត់"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ស្វែងរកផ្លូវកាត់"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"គ្មានលទ្ធផលស្វែងរកទេ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដងអូស"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ការកំណត់ក្ដារចុច"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"កំណត់ផ្លូវកាត់"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"បោះបង់"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ចុចគ្រាប់ចុច"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"កំពុងប្រើបន្សំគ្រាប់ចុចស្រាប់ហើយ។ សាកល្បងប្រើគ្រាប់ចុចផ្សេង។"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"រុករកដោយប្រើក្ដារចុចរបស់អ្នក"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ស្វែងយល់អំពីផ្លូវកាត់ក្ដារចុច"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"រុករកដោយប្រើផ្ទាំងប៉ះរបស់អ្នក"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index f3155704c55f..201c9950741b 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್ಗಳು"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ವಿಜೆಟ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಆ್ಯಪ್ ತೆರೆಯಲು, ಇದು ನೀವೇ ಎಂದು ನೀವು ದೃಢೀಕರಿಸಬೇಕಾಗುತ್ತದೆ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ಅವುಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ಕೆಲವು ವಿಜೆಟ್ಗಳು ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್ಗಾಗಿ ಉದ್ದೇಶಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇಲ್ಲಿ ಸೇರಿಸುವುದು ಸುರಕ್ಷಿತವಲ್ಲದಿರಬಹುದು."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ಅರ್ಥವಾಯಿತು"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್ಡೌನ್ ಮೆನು"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಶನ್ನಲ್ಲಿನ ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ಸಂಭಾಷಣೆ ಅಧಿಸೂಚನೆಗಳ ಮೇಲ್ಭಾಗದಲ್ಲಿ ಹಾಗೂ ಲಾಕ್ ಸ್ಕ್ರೀನ್ನ ಮೇಲೆ ಪ್ರೊಫೈಲ್ ಚಿತ್ರವಾಗಿ ತೋರಿಸುತ್ತದೆ, ಬಬಲ್ನಂತೆ ಗೋಚರಿಸುತ್ತದೆ, ಅಡಚಣೆ ಮಾಡಬೇಡ ಮೋಡ್ಗೆ ಅಡ್ಡಿಯುಂಟುಮಾಡುತ್ತದೆ"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ಆದ್ಯತೆ"</string> <string name="no_shortcut" msgid="8257177117568230126">"ಸಂವಾದ ಫೀಚರ್ಗಳನ್ನು <xliff:g id="APP_NAME">%1$s</xliff:g> ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ಕರೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"ಈ ಗುಂಪಿನ ಅಧಿಸೂಚನೆಗಳನ್ನು ಇಲ್ಲಿ ಕಾನ್ಫಿಗರ್ ಮಾಡಲಾಗಿರುವುದಿಲ್ಲ"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ಅಧಿಕ ರೆಸಲ್ಯೂಷನ್ಗಾಗಿ, ಫೋನ್ ಅನ್ನು ಫ್ಲಿಪ್ ಮಾಡಿ"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ಫೋಲ್ಡ್ ಮಾಡಬಹುದಾದ ಸಾಧನವನ್ನು ಅನ್ಫೋಲ್ಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ಫೋಲ್ಡ್ ಮಾಡಬಹುದಾದ ಸಾಧನವನ್ನು ಸುತ್ತಲೂ ತಿರುಗಿಸಲಾಗುತ್ತಿದೆ"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ಫ್ರಂಟ್ ಸ್ಕ್ರೀನ್ ಆನ್ ಆಗಿದೆ"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ಅನ್ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ನಿಯೋಜಿಸಲು ಕೀಯನ್ನು ಒತ್ತಿರಿ"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ಡ್ರ್ಯಾಗ್ ಹ್ಯಾಂಡಲ್"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ಕೀಬೋರ್ಡ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ಶಾರ್ಟ್ಕಟ್ ಸೆಟ್ ಮಾಡಿ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ರದ್ದುಮಾಡಿ"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ಕೀ ಅನ್ನು ಒತ್ತಿರಿ"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"ಕೀ ಸಂಯೋಜನೆಯು ಈಗಾಗಲೇ ಬಳಕೆಯಲ್ಲಿದೆ. ಮತ್ತೊಂದು ಕೀ ಬಳಸಿ ನೋಡಿ."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಕಲಿಯಿರಿ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ನಿಮ್ಮ ಟಚ್ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 931066b3d749..3303932a5bc2 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"잠금 화면 위젯"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"위젯을 사용하여 앱을 열려면 본인 인증을 해야 합니다. 또한 태블릿이 잠겨 있더라도 누구나 볼 수 있다는 점을 유의해야 합니다. 일부 위젯은 잠금 화면에 적합하지 않고 여기에 추가하기에 안전하지 않을 수 있습니다."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"확인"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"대화 알림 상단에 표시, 잠금 화면에 프로필 사진으로 표시, 대화창으로 표시, 방해 금지 모드를 무시함"</string> <string name="notification_priority_title" msgid="2079708866333537093">"우선순위"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 대화 기능을 지원하지 않습니다."</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"이 알림은 수정할 수 없습니다."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"전화 알림은 수정할 수 없습니다."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"이 알림 그룹은 여기에서 설정할 수 없습니다."</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"해상도를 높이려면 후면 카메라를 사용하세요."</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"폴더블 기기를 펼치는 모습"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"폴더블 기기를 뒤집는 모습"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"전면 화면 켜짐"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"접은 상태"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"펼친 상태"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"접근성"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"단축키 맞춤설정"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"키를 눌러 단축키 지정"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"검색 결과 없음"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"드래그 핸들"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"키보드 설정"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"단축키 설정"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"취소"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"키를 누르세요."</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"이미 사용 중인 키 조합입니다. 다른 키를 사용해 보세요."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"키보드를 사용하여 이동"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키에 관해 알아보세요."</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"터치패드를 사용하여 이동"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index fc16164b6874..fb1b71330c27 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Кулпуланган экрандагы виджеттер"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн өзүңүздү ырасташыңыз керек. Алар кулпуланган планшетиңизде да көрүнүп турат. Кээ бир виджеттерди кулпуланган экранда колдоно албайсыз, андыктан аларды ал жерге кошпой эле койгонуңуз оң."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Cүйлөшүүлөр тууралуу билдирмелердин жогору жагында жана кулпуланган экранда профилдин сүрөтү, ошондой эле калкып чыкма билдирме түрүндө көрүнүп, \"Тынчымды алба\" режимин токтотот"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Маанилүүлүгү"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда оозеки сүйлөшкөнгө болбойт"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Бул билдирмелерди өзгөртүүгө болбойт."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Чалуу билдирмелерин өзгөртүүгө болбойт."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Бул билдирмелердин тобун бул жерде конфигурациялоого болбойт"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Жогорку дааналык үчүн телефондун арткы камерасын колдонуңуз"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ачылып турган бүктөлмө түзмөк"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Оодарылып жаткан бүктөлмө түзмөк"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Маңдайкы экран күйгүзүлдү"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"бүктөлгөн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ачылган"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Атайын мүмкүнчүлүктөр"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Ыкчам баскычтар"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Ыкчам баскычтарды ыңгайлаштыруу"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Ыкчам баскычты дайындоо үчүн баскычты басыңыз"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Эч нерсе табылган жок"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Cүйрөө маркери"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Баскычтоп параметрлери"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ыкчам баскычты тууралоо"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Баскычты басыңыз"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Ачкыч айкалышы колдонулууда. Башка ачкычты байкап көрүңүз."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Керектүү нерселерге баскычтоп аркылуу өтүү"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ыкчам баскычтар тууралуу билип алыңыз"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Керектүү жерге сенсордук такта аркылуу өтөсүз"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 3a8c682bce0e..d2e4763fbcd3 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ສະແດງຢູ່ເທິງສຸດຂອງການແຈ້ງເຕືອນການສົນທະນາ ແລະ ເປັນຮູບໂປຣໄຟລ໌ຢູ່ໜ້າຈໍລັອກ, ປາກົດເປັນຟອງ, ສະແດງໃນໂໝດຫ້າມລົບກວນໄດ້"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ສຳຄັນ"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ຮອງຮັບຄຸນສົມບັດການສົນທະນາ"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ບໍ່ສາມາດແກ້ໄຂການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄດ້."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ບໍ່ສາມາດແກ້ໄຂການແຈ້ງເຕືອນການໂທໄດ້."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"ບໍ່ສາມາດຕັ້ງຄ່າກຸ່ມການແຈ້ງເຕືອນນີ້ຢູ່ບ່ອນນີ້ໄດ້"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"ໃຊ້ໜ້ອຍກວ່າ <xliff:g id="LENGTH">%1$d</xliff:g> ຕົວອັກສອນ"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝາຍເລກສ້າງ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ສຳເນົາໝາຍເລກສ້າງໄປໃສ່ຄລິບບອດແລ້ວ."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"ສຳເນົາໄປໃສ່ຄລິບບອດ."</string> <string name="basic_status" msgid="2315371112182658176">"ເປີດການສົນທະນາ"</string> <string name="select_conversation_title" msgid="6716364118095089519">"ວິດເຈັດການສົນທະນາ"</string> <string name="select_conversation_text" msgid="3376048251434956013">"ແຕະໃສ່ການສົນທະນາໃດໜຶ່ງເພື່ອເພີ່ມມັນໃສ່ໂຮມສະກຣີນຂອງທ່ານ"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ເພື່ອຄວາມລະອຽດທີ່ສູງຂຶ້ນ, ໃຫ້ປີ້ນໂທລະສັບ"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ອຸປະກອນທີ່ພັບໄດ້ກຳລັງກາງອອກ"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ອຸປະກອນທີ່ພັກໄດ້ກຳລັງປີ້ນໄປມາ"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ເປີດໜ້າຈໍດ້ານໜ້າແລ້ວ"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ພັບແລ້ວ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ກາງອອກແລ້ວ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ການຊ່ວຍເຂົ້າເຖິງ"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ປັບແຕ່ງຄີລັດ"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ກົດປຸ່ມເພື່ອກໍານົດທາງລັດ"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ບໍ່ມີຜົນການຊອກຫາ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ບ່ອນຈັບລາກ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ການຕັ້ງຄ່າແປ້ນພິມ"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ຕັ້ງທາງລັດ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ຍົກເລີກ"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ກົດປຸ່ມ"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"ນໍາໃຊ້ປຸ່ມປະສົມຢູ່ແລ້ວ. ໃຫ້ລອງປຸ່ມອື່ນ."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ນຳທາງໂດຍໃຊ້ແປ້ນພິມຂອງທ່ານ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ສຶກສາຄີລັດ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ນຳທາງໂດຍໃຊ້ແຜ່ນສຳຜັດຂອງທ່ານ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index ae85ed2060c6..64f37aec5d77 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -528,6 +528,8 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Užrakinimo ekrano valdikliai"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Kad galėtumėte atidaryti programą naudodami valdiklį, turėsite patvirtinti savo tapatybę. Be to, atminkite, kad bet kas gali peržiūrėti valdiklius net tada, kai planšetinis kompiuteris užrakintas. Kai kurie valdikliai gali būti neskirti jūsų užrakinimo ekranui ir gali būti nesaugu juos čia pridėti."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Supratau"</string> + <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Valdikliai"</string> + <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Jei norite pridėti valdiklių šaukinį užrakinimo ekrane, įsitikinkite, kad tai įgalinta nustatymuose."</string> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string> @@ -674,7 +676,7 @@ <string name="screen_pinning_exit" msgid="4553787518387346893">"Programa atsegta"</string> <string name="stream_voice_call" msgid="7468348170702375660">"Skambutis"</string> <string name="stream_system" msgid="7663148785370565134">"Sistema"</string> - <string name="stream_ring" msgid="7550670036738697526">"Skambutis"</string> + <string name="stream_ring" msgid="7550670036738697526">"Skambėjimas"</string> <string name="stream_music" msgid="2188224742361847580">"Medija"</string> <string name="stream_alarm" msgid="16058075093011694">"Signalas"</string> <string name="stream_notification" msgid="7930294049046243939">"Pranešimas"</string> @@ -780,6 +782,7 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Rodoma pokalbių pranešimų viršuje ir kaip profilio nuotrauka užrakinimo ekrane, burbule, pertraukia netrukdymo režimą"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetiniai"</string> <string name="no_shortcut" msgid="8257177117568230126">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ nepalaiko pokalbių funkcijų"</string> + <string name="notification_guts_bundle_feedback" msgid="5393570876655201459">"Pateikti atsiliepimą apie rinkinį"</string> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Šių pranešimų keisti negalima."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Skambučių pranešimų keisti negalima."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Šios grupės pranešimai čia nekonfigūruojami"</string> @@ -1357,8 +1360,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Kad raiška būtų geresnė, apverskite telefoną"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Lankstomasis įrenginys išlankstomas"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Lankstomasis įrenginys apverčiamas"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Priekinis ekranas įjungtas"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sulenkta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"nesulenkta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1420,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pritaikomumas"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sparčiųjų klavišų tinkinimas"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Paspauskite klavišą, kad priskirtumėte spartųjį klavišą"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ieškoti sparčiųjų klavišų"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nėra jokių paieškos rezultatų"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string> @@ -1431,9 +1438,14 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkimo rankenėlė"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatūros nustatymai"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nustatyti spartųjį klavišą"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atšaukti"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paspauskite klavišą"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Klavišų derinys jau naudojamas. Bandykite naudoti kitą klavišą."</string> + <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klavišų derinys jau naudojamas. Bandykite naudoti kitą klavišą."</string> + <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Sparčiojo klavišo nustatyti negalima."</string> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naršykite naudodamiesi klaviatūra"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Sužinokite apie sparčiuosius klavišus"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naršykite naudodamiesi jutikline dalimi"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index b20ab36a3896..dc1c4ba28d0b 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Bloķēšanas ekrāna logrīki"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Lai atvērtu lietotni, izmantojot logrīku, jums būs jāapstiprina sava identitāte. Turklāt ņemiet vērā, ka ikviens var skatīt logrīkus, pat ja planšetdators ir bloķēts. Iespējams, daži logrīki nav paredzēti izmantošanai bloķēšanas ekrānā, un var nebūt droši tos šeit pievienot."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Labi"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Parādās sarunu paziņojumu augšdaļā un kā profila attēls bloķēšanas ekrānā, arī kā burbulis, pārtrauc režīmu “Netraucēt”."</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritārs"</string> <string name="no_shortcut" msgid="8257177117568230126">"Lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g> netiek atbalstītas sarunu funkcijas."</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Šos paziņojumus nevar modificēt."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Paziņojumus par zvaniem nevar modificēt."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Šeit nevar konfigurēt šo paziņojumu grupu."</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Lai izmantotu augstāku izšķirtspēju, apvērsiet tālruni"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Salokāma ierīce tiek atlocīta"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Salokāma ierīce tiek apgriezta"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Priekšējais ekrāns ir ieslēgts"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"aizvērta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"atvērta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Īsinājumtaustiņu pielāgošana"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Lai piešķirtu īsinājumtaustiņu, nospiediet taustiņu"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēt saīsnes"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nav meklēšanas rezultātu"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkšanas turis"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatūras iestatījumi"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Iestatīt īsinājumtaustiņu"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atcelt"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nospiediet taustiņu"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Taustiņu kombinācija jau tiek izmantota. Izmēģiniet citu taustiņu."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pārvietošanās, izmantojot tastatūru"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Uzziniet par īsinājumtaustiņiem."</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pārvietošanās, izmantojot skārienpaliktni"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index f8bfcf9296ca..94f295fbcddb 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети на заклучен екран"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијава ќе се избришат."</string> @@ -698,7 +702,7 @@ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контрола на шум"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Просторен звук"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Исклучено"</string> - <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Фиксно"</string> + <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Фиксирано"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Следење на главата"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Допрете за да го промените режимот на ѕвончето"</string> <string name="volume_ringer_mode" msgid="6867838048430807128">"режим на ѕвонче"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Се прикажува најгоре во известувањата за разговор и како профилна слика на заклучен екран, се појавува како балонче, го прекинува „Не вознемирувај“"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритетно"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддржува функции за разговор"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Овие известувања не може да се изменат"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Известувањата за повици не може да се изменат."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Оваа група известувања не може да се конфигурира тука"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Отворете го телефонот за да добиете повисока резолуција"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Преклопувачки уред се отклопува"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Преклопувачки уред се врти"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Предниот екран е вклучен"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворен"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворен"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Пристапност"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Приспособете ги кратенките од тастатурата"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Притиснете го копчето за да доделите кратенка"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пребарувајте кратенки"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултати од пребарување"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Поставете кратенка"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притиснете го копчето"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Комбинацијата на копчиња веќе се користи. Обидете се со друго копче."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Движете се со користење на тастатурата"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете ги кратенките од тастатурата"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Движете се со користење на допирната подлога"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 54364c804581..85bc7017b378 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ലോക്ക് സ്ക്രീൻ വിജറ്റുകൾ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"വിജറ്റ് ഉപയോഗിച്ച് ഒരു ആപ്പ് തുറക്കാൻ, ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. നിങ്ങളുടെ ടാബ്ലെറ്റ് ലോക്കായിരിക്കുമ്പോഴും എല്ലാവർക്കും അത് കാണാനാകുമെന്നതും ഓർക്കുക. ചില വിജറ്റുകൾ നിങ്ങളുടെ ലോക്ക് സ്ക്രീനിന് ഉള്ളതായിരിക്കില്ല, അവ ഇവിടെ ചേർക്കുന്നത് സുരക്ഷിതവുമായിരിക്കില്ല."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"മനസ്സിലായി"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"സംഭാഷണ അറിയിപ്പുകളുടെ മുകളിലും സ്ക്രീൻ ലോക്കായിരിക്കുമ്പോൾ ഒരു പ്രൊഫൈൽ ചിത്രമായും ബബിൾ രൂപത്തിൽ ദൃശ്യമാകുന്നു, ശല്യപ്പെടുത്തരുത് മോഡ് തടസ്സപ്പെടുത്തുന്നു"</string> <string name="notification_priority_title" msgid="2079708866333537093">"മുൻഗണന"</string> <string name="no_shortcut" msgid="8257177117568230126">"സംഭാഷണ ഫീച്ചറുകളെ <xliff:g id="APP_NAME">%1$s</xliff:g> പിന്തുണയ്ക്കുന്നില്ല"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ഈ അറിയിപ്പുകൾ പരിഷ്ക്കരിക്കാനാവില്ല."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"കോൾ അറിയിപ്പുകൾ പരിഷ്കരിക്കാനാകുന്നില്ല."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"അറിയിപ്പുകളുടെ ഈ ഗ്രൂപ്പ് ഇവിടെ കോണ്ഫിഗര് ചെയ്യാൻ കഴിയില്ല"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"<xliff:g id="LENGTH">%1$d</xliff:g>-ൽ കുറവ് പ്രതീകങ്ങൾ ഉപയോഗിക്കുക"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ബിൽഡ് നമ്പർ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ക്ലിപ്പ്ബോർഡിലേക്ക് ബിൽഡ് നമ്പർ പകർത്തി."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തുക."</string> <string name="basic_status" msgid="2315371112182658176">"സംഭാഷണം തുറക്കുക"</string> <string name="select_conversation_title" msgid="6716364118095089519">"സംഭാഷണ വിജറ്റുകൾ"</string> <string name="select_conversation_text" msgid="3376048251434956013">"നിങ്ങളുടെ ഹോം സ്ക്രീനിൽ ചേർക്കാൻ സംഭാഷണത്തിൽ ടാപ്പ് ചെയ്യുക"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ഉയർന്ന റെസല്യൂഷന്, ഫോൺ ഫ്ലിപ്പ് ചെയ്യുക"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ഫോൾഡ് ചെയ്യാവുന്ന ഉപകരണം അൺഫോൾഡ് ആകുന്നു"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ഫോൾഡ് ചെയ്യാവുന്ന ഉപകരണം, കറങ്ങുന്ന വിധത്തിൽ ഫ്ലിപ്പ് ആകുന്നു"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ഫ്രണ്ട് സ്ക്രീൻ ഓണാക്കി"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ഫോൾഡ് ചെയ്തത്"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"അൺഫോൾഡ് ചെയ്തത്"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ഉപയോഗസഹായി"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"കീബോഡ് കുറുക്കുവഴികൾ"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"കീബോർഡ് കുറുക്കുവഴികൾ ഇഷ്ടാനുസൃതമാക്കുക"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"കുറുക്കുവഴി അസൈൻ ചെയ്യാൻ കീ അമർത്തുക"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"തിരയൽ ഫലങ്ങളൊന്നുമില്ല"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"കീബോർഡ് ക്രമീകരണം"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"കുറുക്കുവഴി സജ്ജീകരിക്കുക"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"റദ്ദാക്കുക"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"കീ അമർത്തുക"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"കീ കോമ്പിനേഷൻ ഇതിനകം ഉപയോഗത്തിലുണ്ട്. മറ്റൊരു കീ പരീക്ഷിക്കുക."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"കീബോർഡ് കുറുക്കുവഴികൾ മനസ്സിലാക്കുക"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"നിങ്ങളുടെ ടച്ച്പാഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 0e2dc23e88c1..78b2ddab02ac 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Түгжээтэй дэлгэцийн виджет"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Харилцан ярианы мэдэгдлийн дээд талд болон түгжигдсэн дэлгэц дээр профайл зураг байдлаар харуулах бөгөөд бөмбөлөг хэлбэрээр харагдана. Бүү саад бол горимыг тасалдуулна"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Чухал"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь харилцан ярианы онцлогуудыг дэмждэггүй"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Эдгээр мэдэгдлийг өөрчлөх боломжгүй."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Дуудлагын мэдэгдлийг өөрчлөх боломжгүй."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Энэ бүлэг мэдэгдлийг энд тохируулах боломжгүй байна"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Илүү өндөр нягтрал авах бол утсыг хөнтөрнө үү"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Эвхэгддэг төхөөрөмжийг дэлгэж байна"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Эвхэгддэг төхөөрөмжийг хөнтөрч байна"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Нүүрэн талын дэлгэцийг асаасан"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"эвхсэн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"дэлгэсэн"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Хандалт"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Товчлуурын шууд холбоосыг өөрчлөх"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Товчлол оноохын тулд товч дарна уу"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ямар ч хайлтын илэрц байхгүй"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Гарын тохиргоо"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Товчлол тохируулах"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Цуцлах"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Товч дарна уу"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Товчийн хослолыг аль хэдийн ашиглаж байна. Өөр товч туршиж үзнэ үү."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Гараа ашиглан шилжих"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Товчлуурын шууд холбоосыг мэдэж аваарай"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Мэдрэгч самбараа ашиглан шилжээрэй"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index cd9062252425..280a757d1661 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट वापरून अॅप उघडण्यासाठी, तुम्हाला हे तुम्हीच असल्याची पडताळणी करावी लागेल. तसेच, लक्षात ठेवा, तुमचा टॅबलेट लॉक असतानादेखील कोणीही ती पाहू शकते. काही विजेट कदाचित तुमच्या लॉक स्क्रीनसाठी नाहीत आणि ती इथे जोडणे असुरक्षित असू शकते."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"समजले"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"संभाषण सूचनांच्या वरती आणि लॉक स्क्रीनवरील प्रोफाइल फोटो म्हणून दिसते, बबल म्हणून दिसते, व्यत्यय आणू नका यामध्ये अडथळा आणते"</string> <string name="notification_priority_title" msgid="2079708866333537093">"प्राधान्य"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे संभाषण वैशिष्ट्यांना सपोर्ट करत नाही"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कॉलशी संबंधित सूचनांमध्ये फेरबदल केला जाऊ शकत नाही."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"उच्च रेझोल्यूशनसाठी, फोन फ्लिप करा"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फोल्ड करता येण्यासारखे डिव्हाइस अनफोल्ड केले जात आहे"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फोल्ड करता येण्यासारखे डिव्हाइस आजूबाजूला फ्लिप केले जात आहे"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"पुढील स्क्रीन सुरू केलेली आहे"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड केलेले"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"फोल्ड न केलेले"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"अॅक्सेसिबिलिटी"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट कस्टमाइझ करा"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"शॉर्टकट असाइन करण्यासाठी की प्रेस करा"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कोणतेही शोध परिणाम नाहीत"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्रॅग हॅंडल"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग्ज"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"शॉर्टकट सेट करा"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करा"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की प्रेस करा"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"की कॉम्बिनेशन आधीपासून वापरले जात आहे. दुसरी की वापरून पहा."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"तुमचा कीबोर्ड वापरून नेव्हिगेट करा"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट जाणून घ्या"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"तुमचा टचपॅड वापरून नेव्हिगेट करा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index c108fc476da0..d20262ace5d2 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget skrin kunci"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka apl menggunakan widget, anda perlu mengesahkan identiti anda. Selain itu, perlu diingat bahawa sesiapa sahaja boleh melihat widget tersebut, walaupun semasa tablet anda dikunci. Sesetengah widget mungkin tidak sesuai untuk skrin kunci anda dan mungkin tidak selamat untuk ditambahkan di sini."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Ditunjukkan di bahagian atas pemberitahuan perbualan dan sebagai gambar profil pada skrin kunci, muncul sebagai gelembung, mengganggu Jangan Ganggu"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Keutamaan"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak menyokong ciri perbualan"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Pemberitahuan ini tidak boleh diubah suai."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Pemberitahuan panggilan tidak boleh diubah suai."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Kumpulan pemberitahuan ini tidak boleh dikonfigurasikan di sini"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Untuk peleraian lebih tinggi, balikkan telefon"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Peranti boleh lipat dibuka"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Peranti boleh lipat diterbalikkan"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Skrin hadapan dihidupkan"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"terlipat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tidak terlipat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kebolehaksesan"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sesuaikan pintasan papan kekunci"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Tekan kekunci untuk menetapkan pintasan"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tiada hasil carian"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string> @@ -1431,14 +1441,21 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Pemegang seret"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tetapan Papan Kekunci"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tetapkan pintasan"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan kekunci"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Gabungan kekunci sudah digunakan. Cuba kekunci lain."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigasi menggunakan papan kekunci"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ketahui pintasan papan kekunci"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigasi menggunakan pad sentuh anda"</string> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Ketahui gerak isyarat pad sentuh"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigasi menggunakan papan kekunci dan pad sentuh anda"</string> + <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigasi menggunakan papan kekunci dan pad sentuh"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Ketahui gerak isyarat pad sentuh, pintasan papan kekunci dan pelbagai lagi"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kembali"</string> <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Akses laman utama"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index ce52f4e4e024..b77f0374e065 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"စကားဝိုင်း အကြောင်းကြားချက်များ၏ ထိပ်ပိုင်းနှင့် ပရိုဖိုင်ပုံအဖြစ် လော့ခ်မျက်နှာပြင်တွင် ပြသည်။ ပူဖောင်းကွက်အဖြစ် မြင်ရပြီး ‘မနှောင့်ယှက်ရ’ ကို ကြားဖြတ်သည်"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ဦးစားပေး"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> က စကားဝိုင်းဝန်ဆောင်မှုများကို မပံ့ပိုးပါ"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ဤအကြောင်းကြားချက်များကို ပြုပြင်၍ မရပါ။"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ခေါ်ဆိုမှုအကြောင်းကြားချက်များကို ပြင်၍မရပါ။"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"ဤအကြောင်းကြားချက်အုပ်စုကို ဤနေရာတွင် စီစဉ်သတ်မှတ်၍ မရပါ"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ပုံရိပ် ပိုမိုပြတ်သားစေရန် ဖုန်းကို တစ်ဖက်သို့ လှန်လိုက်ပါ"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ခေါက်နိုင်သောစက်ကို ဖြန့်လိုက်သည်"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ခေါက်နိုင်သောစက်ကို တစ်ဘက်သို့ လှန်လိုက်သည်"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ရှေ့စခရင် ဖွင့်ထားသည်"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ခေါက်ထားသည်"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ဖြန့်ထားသည်"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"အများသုံးနိုင်မှု"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"လက်ကွက်ဖြတ်လမ်းများ"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"လက်ကွက်ဖြတ်လမ်းများကို စိတ်ကြိုက်လုပ်ခြင်း"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ဖြတ်လမ်းသတ်မှတ်ရန် ကီးကို နှိပ်ပါ"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ဖြတ်လမ်းများ ရှာရန်"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ရှာဖွေမှုရလဒ် မရှိပါ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ဖြတ်လမ်း သတ်မှတ်ရန်"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"မလုပ်တော့"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ကီးကို နှိပ်ပါ"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"ကီးပေါင်းစပ်ခြင်းကို သုံးနေပြီးဖြစ်သည်။ အခြားကီးကို စမ်းကြည့်ပါ။"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"သင့်ကီးဘုတ်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"လက်ကွက်ဖြတ်လမ်းများကို လေ့လာပါ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"သင့်တာ့ချ်ပက်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index db352a5fc4b0..fa4886b93dcc 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Låseskjermmoduler"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"For å åpne en app ved hjelp av en modul må du bekrefte at det er deg. Husk også at hvem som helst kan se dem, selv om nettbrettet er låst. Noen moduler er kanskje ikke laget for å være på låseskjermen og kan være utrygge å legge til der."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Greit"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Vises øverst på samtalevarsler og som et profilbilde på låseskjermen, vises som en boble, avbryter «Ikke forstyrr»"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> støtter ikke samtalefunksjoner"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Disse varslene kan ikke endres."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Anropsvarsler kan ikke endres."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Denne varselgruppen kan ikke konfigureres her"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Brett ut telefonen for å få høyere oppløsning"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En foldbar enhet blir brettet ut"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En foldbar enhet blir snudd"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Frontskjermen er slått på"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"lagt sammen"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"åpen"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpass hurtigtastene"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Trykk på tasten for å tilordne hurtigtasten"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ingen søkeresultater"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtak"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturinnstillinger"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Angi hurtigtast"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Trykk på tasten"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Tastekombinasjonen brukes allerede. Prøv en annen tast."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger med tastaturet"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lær deg hurtigtaster"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger med styreflaten"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 2c1d7271ac19..babc0d19b781 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लक स्क्रिन विजेटहरू"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"यो वार्तालापका सूचनाहरूको सिरानमा, बबलका रूपमा र लक स्क्रिनमा प्रोफाइल फोटोका रूपमा देखिन्छ। साथै, यसले गर्दा \'बाधा नपुऱ्याउनुहोस्\' नामक सुविधामा अवरोध आउँछ"</string> <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा वार्तालापसम्बन्धी सुविधा प्रयोग गर्न मिल्दैन"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"यी सूचनाहरू परिमार्जन गर्न मिल्दैन।"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कलसम्बन्धी सूचनाहरू परिमार्जन गर्न मिल्दैन।"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"यहाँबाट सूचनाहरूको यो समूह कन्फिगर गर्न सकिँदैन"</string> @@ -1355,10 +1361,9 @@ <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"स्क्रिनहरू बदल्ने हो?"</string> <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"उच्च रिजोल्युसनको सेल्फी खिच्न पछाडिको क्यामेरा प्रयोग गर्नुहोस्"</string> <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"उच्च रिजोल्युसनको सेल्फी खिच्न फोन फ्लिप गर्नुहोस्"</string> - <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फोल्ड गर्न मिल्ने डिभाइस अनफोल्ड गरेको देखाइएको एनिमेसन"</string> - <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फोल्ड गर्न मिल्ने डिभाइस यताउता पल्टाएर देखाइएको एनिमेसन"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फोल्डेबल डिभाइस अनफोल्ड गरेको देखाइएको एनिमेसन"</string> + <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फोल्डेबल डिभाइस यताउता पल्टाएर देखाइएको एनिमेसन"</string> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"अगाडिको स्क्रिन अन गरिएको छ"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड गरिएको"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"अनफोल्ड गरिएको"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सर्वसुलभता"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"किबोर्डका सर्टकटहरू कस्टमाइज गर्नुहोस्"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"सर्टकट असाइन गर्न की थिच्नुहोस्"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कुनै पनि खोज परिणाम भेटिएन"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"किबोर्डसम्बन्धी सेटिङ"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"सर्टकट सेट गर्नुहोस्"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द गर्नुहोस्"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की थिच्नुहोस्"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"यो की कम्बिनेसन प्रयोग गरिसकिएको छ। अर्कै की प्रयोग गरी हेर्नुहोस्।"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"किबोर्ड प्रयोग गरी नेभिगेट गर्नुहोस्"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"किबोर्डका सर्टकटहरू प्रयोग गर्न सिक्नुहोस्"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 0d195a3b40d7..61c4bd545cb1 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets op het vergrendelscherm"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Wordt getoond bovenaan gespreksmeldingen en als profielfoto op het vergrendelscherm, verschijnt als bubbel, onderbreekt Niet storen"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Deze meldingen kunnen niet worden aangepast."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Gespreksmeldingen kunnen niet worden aangepast."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Deze groep meldingen kan hier niet worden ingesteld"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Gebruik minder dan <xliff:g id="LENGTH">%1$d</xliff:g> tekens"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummer naar klembord gekopieerd."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"kopiëren naar klembord."</string> <string name="basic_status" msgid="2315371112182658176">"Gesprek openen"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Gesprekswidgets"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Tik op een gesprek om het toe te voegen aan je startscherm"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Draai de telefoon om voor een hogere resolutie"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Opvouwbaar apparaat wordt uitgevouwen"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Opvouwbaar apparaat wordt gedraaid"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Scherm aan voorzijde aangezet"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dichtgevouwen"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opengevouwen"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toegankelijkheid"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sneltoetsen aanpassen"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Druk op de toets om de sneltoets toe te wijzen"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sneltoetsen zoeken"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen zoekresultaten"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handgreep voor slepen"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Toetsenbordinstellingen"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Sneltoets instellen"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuleren"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk op een toets"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Toetsencombinatie is al in gebruik. Probeer een andere toets."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeren met je toetsenbord"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer sneltoetsen die je kunt gebruiken"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeren met je touchpad"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 181315ba045c..e932e74cad0e 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍ ବଦଳାନ୍ତୁ"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପରିବର୍ତ୍ତନ କରିହେବ ନାହିଁ।"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"କଲ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ପରିବର୍ତ୍ତନ କରାଯାଇପାରିବ ନାହିଁ।"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"ଏଠାରେ ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଗ୍ରୁପ୍ କନଫ୍ୟୁଗର୍ କରାଯାଇପାରିବ ନାହିଁ"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ଉଚ୍ଚ ରିଜୋଲ୍ୟୁସନ ପାଇଁ ଫୋନକୁ ଫ୍ଲିପ କରନ୍ତୁ"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ଫୋଲ୍ଡ କରାଯାଇପାରୁଥିବା ଡିଭାଇସକୁ ଅନଫୋଲ୍ଡ କରାଯାଉଛି"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ଫୋଲ୍ଡ କରାଯାଇପାରୁଥିବା ଡିଭାଇସକୁ ଫ୍ଲିପ କରାଯାଉଛି"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ସାମ୍ନା ସ୍କ୍ରିନ ଚାଲୁ ଅଛି"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ଫୋଲ୍ଡେଡ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ଅନଫୋଲ୍ଡେଡ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ଆକ୍ସେସିବିଲିଟୀ"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ସର୍ଟକଟ ଆସାଇନ କରିବା ପାଇଁ କୀ\'କୁ ଦବାନ୍ତୁ"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"କୌଣସି ସର୍ଚ୍ଚ ଫଳାଫଳ ନାହିଁ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"କୀବୋର୍ଡ ସେଟିଂ"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ସର୍ଟକଟ ସେଟ କରନ୍ତୁ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ବାତିଲ କରନ୍ତୁ"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"କୀ ଦବାନ୍ତୁ"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"କୀ କମ୍ବିନେସନ ପୂର୍ବରୁ ବ୍ୟବହାର କରାଯାଉଛି। ଅନ୍ୟ ଏକ କୀ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ।"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ଆପଣଙ୍କ କୀବୋର୍ଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 5b9919ef97b6..80798b714d12 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ, ਜੋ ਕਿ ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ ਅਤੇ \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵਿਘਨ ਵੀ ਪਾ ਸਕਦੀਆਂ ਹਨ"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ਤਰਜੀਹ"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਗੱਲਬਾਤ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਸੋਧਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ਕਾਲ ਸੰਬੰਧੀ ਸੂਚਨਾਵਾਂ ਨੂੰ ਸੋਧਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"ਇਹ ਸੂਚਨਾਵਾਂ ਦਾ ਗਰੁੱਪ ਇੱਥੇ ਸੰਰੂਪਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ਉੱਚ ਰੈਜ਼ੋਲਿਊਸ਼ਨ ਲਈ, ਫ਼ੋਨ ਨੂੰ ਫਲਿੱਪ ਕਰੋ"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ਮੋੜਨਯੋਗ ਡੀਵਾਈਸ ਨੂੰ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ਮੋੜਨਯੋਗ ਡੀਵਾਈਸ ਨੂੰ ਆਲੇ-ਦੁਆਲੇ ਫਲਿੱਪ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ਅਗਲੀ ਸਕ੍ਰੀਨ ਚਾਲੂ ਕੀਤੀ ਗਈ"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ਅਣਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ਪਹੁੰਚਯੋਗਤਾ"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ਸ਼ਾਰਟਕੱਟ ਨਿਰਧਾਰਿਤ ਕਰਨ ਲਈ ਕੁੰਜੀ ਦਬਾਓ"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਸ਼ਾਰਟਕੱਟ ਖੋਜੋ"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ਕੋਈ ਖੋਜ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ਕੀ-ਬੋਰਡ ਸੈਟਿੰਗਾਂ"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ਸ਼ਾਰਟਕੱਟ ਸੈੱਟ ਕਰੋ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ਰੱਦ ਕਰੋ"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ਕੁੰਜੀ ਦਬਾਓ"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"ਕੁੰਜੀ ਸੁਮੇਲ ਪਹਿਲਾਂ ਹੀ ਵਰਤੋਂ ਵਿੱਚ ਹੈ। ਕੋਈ ਹੋਰ ਕੁੰਜੀ ਨੂੰ ਵਰਤ ਕੇ ਦੇਖੋ।"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ਕੀ-ਬੋਰਡ ਦੇ ਸ਼ਾਰਟਕੱਟਾਂ ਬਾਰੇ ਜਾਣੋ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index e41d16f82785..67fdf8c2c383 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widżety na ekranie blokady"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aby otworzyć aplikację za pomocą widżetu, musisz potwierdzić swoją tożsamość. Pamiętaj też, że każdy będzie mógł wyświetlić widżety nawet wtedy, gdy tablet będzie zablokowany. Niektóre widżety mogą nie być przeznaczone do umieszczenia na ekranie blokady i ich dodanie w tym miejscu może być niebezpieczne."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Wyświetla się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady, jako dymek, przerywa działanie trybu Nie przeszkadzać"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priorytetowe"</string> <string name="no_shortcut" msgid="8257177117568230126">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> nie obsługuje funkcji rozmów"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Tych powiadomień nie można zmodyfikować."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Powiadomień o połączeniach nie można modyfikować."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Tej grupy powiadomień nie można tu skonfigurować"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Wpisz mniej znaków niż <xliff:g id="LENGTH">%1$d</xliff:g>"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"Kopiuj do schowka"</string> <string name="basic_status" msgid="2315371112182658176">"Otwarta rozmowa"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Widżety rozmów"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Kliknij rozmowę, aby dodać ją do ekranu głównego"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Odwróć telefon, aby uzyskać wyższą rozdzielczość"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Składane urządzenie jest rozkładane"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Składane urządzenie jest obracane"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Ekran przedni jest włączony"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"po zamknięciu"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"po otwarciu"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ułatwienia dostępu"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Dostosuj skróty klawiszowe"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Naciśnij klawisz, aby przypisać skrót"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Brak wyników wyszukiwania"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Uchwyt do przeciągania"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ustawienia klawiatury"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ustaw skrót"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anuluj"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Naciśnij klawisz"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinacja klawiszy jest już używana. Użyj innego klawisza."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nawiguj za pomocą klawiatury"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Dowiedz się więcej o skrótach klawiszowych"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nawiguj za pomocą touchpada"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 72e593c06718..7db8d8dd5fcb 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparecem na parte superior das notificações de conversa, como uma foto do perfil na tela de bloqueio e como um balão. Interrompem o Não perturbe."</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string> <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar essas notificações."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Não é possível modificar as notificações de chamada."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Não é possível configurar esse grupo de notificações aqui"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Para uma resolução maior, vire o smartphone"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Tela frontal ativada"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pressione a tecla para atribuir o atalho"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Essa combinação de teclas já está em uso. Tente outra tecla."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 460d55304e1e..d9d82bffd294 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -528,6 +528,8 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets do ecrã de bloqueio"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir uma app através de um widget, vai ter de validar a sua identidade. Além disso, tenha em atenção que qualquer pessoa pode ver os widgets, mesmo quando o tablet estiver bloqueado. Alguns widgets podem não se destinar ao ecrã de bloqueio e pode ser inseguro adicioná-los aqui."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string> + <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para adicionar widgets ao ecrã de bloqueio como um atalho, certifique-se de que estão ativados nas definições."</string> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string> @@ -780,6 +782,7 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparece na parte superior das notificações de conversas e como uma imagem do perfil no ecrã de bloqueio, surge como um balão, interrompe o modo Não incomodar"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> <string name="no_shortcut" msgid="8257177117568230126">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> não suporta funcionalidades de conversa."</string> + <string name="notification_guts_bundle_feedback" msgid="5393570876655201459">"Enviar feedback sobre o pacote"</string> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar estas notificações."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Não é possível modificar as notificações de chamadas."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Não é possível configurar este grupo de notificações aqui."</string> @@ -1220,8 +1223,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Use menos de <xliff:g id="LENGTH">%1$d</xliff:g> carateres"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"copiar para a área de transferência."</string> <string name="basic_status" msgid="2315371112182658176">"Abrir conversa"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Widgets de conversa"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Toque numa conversa para a adicionar ao ecrã principal"</string> @@ -1357,8 +1359,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Para uma resolução superior, inverta o telemóvel"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável a ser desdobrado"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável a ser virado ao contrário"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Ecrã frontal ativado"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1419,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalize os atalhos de teclado"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Prima a tecla para atribuir o atalho"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado da pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string> @@ -1431,9 +1437,13 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Indicador para arrastar"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Definições do teclado"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Configurar atalho"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Prima a tecla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"A combinação de teclas já está a ser usada. Experimente outra tecla."</string> + <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"A combinação de teclas já está a ser usada. Experimente outra tecla."</string> + <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Não é possível definir o atalho."</string> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue com o teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos de teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue com o touchpad"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 72e593c06718..7db8d8dd5fcb 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Aparecem na parte superior das notificações de conversa, como uma foto do perfil na tela de bloqueio e como um balão. Interrompem o Não perturbe."</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string> <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar essas notificações."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Não é possível modificar as notificações de chamada."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Não é possível configurar esse grupo de notificações aqui"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Para uma resolução maior, vire o smartphone"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Tela frontal ativada"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pressione a tecla para atribuir o atalho"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Essa combinação de teclas já está em uso. Tente outra tecla."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 42fd14b1b9c2..60e5b8a4e163 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeturi pe ecranul de blocare"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Se afișează în partea de sus a notificărilor pentru conversații și ca fotografie de profil pe ecranul de blocare, apare ca un balon, întrerupe funcția Nu deranja"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritate"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu acceptă funcții pentru conversații"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Aceste notificări nu pot fi modificate."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Notificările pentru apeluri nu pot fi modificate."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Acest grup de notificări nu poate fi configurat aici"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Pentru o rezoluție mai mare, deschide telefonul"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispozitiv pliabil care este desfăcut"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispozitiv pliabil care este întors"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Ecranul frontal este activat"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"închis"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"deschis"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizează comenzile rapide de la tastatură"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Apasă tasta pentru a atribui comanda rapidă"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Niciun rezultat al căutării"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ghidaj de tragere"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setările tastaturii"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setează o comandă rapidă"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulează"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Apasă tasta"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Combinația de taste este deja folosită. Încearcă altă tastă."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navighează folosind tastatura"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Învață comenzile rapide de la tastatură"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navighează folosind touchpadul"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 866cb5e8f602..2707e4b4873d 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виджеты на заблокированном экране"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Чтобы открыть приложение, используя виджет, вам нужно будет подтвердить свою личность. Обратите внимание, что виджеты видны всем, даже если планшет заблокирован. Некоторые виджеты не предназначены для использования на заблокированном экране. Добавлять их туда может быть небезопасно."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ОК"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string> @@ -698,8 +702,8 @@ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контроль шума"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Пространственное звучание"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Отключено"</string> - <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Без отслеживания"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"С отслеживанием"</string> + <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Статичное"</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Динамичное"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Нажмите, чтобы изменить режим звонка."</string> <string name="volume_ringer_mode" msgid="6867838048430807128">"режим звонка"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Появляется в верхней части уведомлений о сообщениях, в виде всплывающего чата, а также в качестве фото профиля на заблокированном экране, прерывает режим \"Не беспокоить\"."</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> <string name="no_shortcut" msgid="8257177117568230126">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает функции разговоров."</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Эти уведомления нельзя изменить."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Уведомления о звонках нельзя изменить."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Эту группу уведомлений нельзя настроить здесь."</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Переверните телефон и используйте основную камеру, чтобы делать снимки с более высоким разрешением."</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Складное устройство в разложенном виде"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Перевернутое складное устройство"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Передний экран включен"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"устройство сложено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"устройство разложено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Специальные возможности"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Как настроить быстрые клавиши"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Нажмите клавишу, чтобы назначить быструю команду."</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ничего не найдено"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string> @@ -1431,15 +1441,22 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки клавиатуры"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Задать сочетание клавиш"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отмена"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Нажмите клавишу"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Это сочетание клавиш уже используется. Попробуйте другое."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигация с помощью клавиатуры"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Узнайте о сочетаниях клавиш."</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигация с помощью сенсорной панели"</string> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Узнайте о жестах на сенсорной панели."</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигация с помощью клавиатуры и сенсорной панели"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Узнайте о жестах на сенсорной панели, сочетаниях клавиш и многом другом."</string> + <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Выучите жесты на сенсорной панели, сочетания клавиш и другие варианты навигации."</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string> <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главный экран"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Просмотр недавних приложений"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 0ccd61c41905..761d8febff9b 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"අගුළු තිර විජට්"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්යාපනය කිරීමට අවශ්ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"සංවාද දැනුම්දීම්වල ඉහළින්ම සහ අගුලු තිරයේ ඇති පැතිකඩ පින්තූරයක් ලෙස පෙන්වයි, බුබුළක් ලෙස දිස් වේ, බාධා නොකරන්න සඳහා බාධා කරයි"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ප්රමුඛතාව"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> සංවාද විශේෂාංගවලට සහාය නොදක්වයි"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"මෙම දැනුම්දීම් වෙනස් කළ නොහැක."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"ඇමතුම් දැනුම්දීම් වෙනස් කළ නොහැකිය."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"මෙම දැනුම්දීම් සමූහය මෙහි වින්යාස කළ නොහැක"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"ඉහළ විභේදනය සඳහා, දුරකථනය හරවන්න"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"දිග හැරෙමින් පවතින නැමිය හැකි උපාංගය"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"වටා පෙරළෙමින් තිබෙන නැමිය හැකි උපාංගය"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ඉදිරිපස තිරය ක්රියාත්මකයි"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"නැවූ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"නොනැවූ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ප්රවේශ්යතාව"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"යතුරුපුවරු කෙටිමං අභිරුචිකරණය කරන්න"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"කෙටි මග පැවරීමට යතුර ඔබන්න"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"සෙවීම් ප්රතිඵල නැත"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"යතුරු පුවරු සැකසීම්"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"කෙටිමඟ සකසන්න"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"අවලංගු කරන්න"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"යතුර ඔබන්න"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"යතුරු සංයෝජනය දැනටමත් භාවිත වේ. වෙනත් යතුරක් උත්සාහ කරන්න."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ඔබේ යතුරු පුවරුව භාවිතයෙන් සංචාලනය කරන්න"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"යතුරුපුවරු කෙටිමං ඉගෙන ගන්න"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ඔබේ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index cc59beafe025..88aa12d275e2 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikácie na uzamknutej obrazovke"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ak chcete otvoriť aplikáciu pomocou miniaplikácie, budete musieť overiť svoju totožnosť. Pamätajte, že si miniaplikáciu môže pozrieť ktokoľvek, aj keď máte tablet uzamknutý. Niektoré miniaplikácie možno nie sú určené pre uzamknutú obrazovku a ich pridanie tu môže byť nebezpečné."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Dobre"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Zobrazuje sa ako bublina v hornej časti upozornení konverzácie a profilová fotka na uzamknutej obrazovke, preruší režim bez vyrušení"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritné"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nepodporuje funkcie konverzácie"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Tieto upozornenia sa nedajú upraviť."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Upozornenia na hovory sa nedajú upraviť."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Túto skupinu upozornení nejde na tomto mieste konfigurovať"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Ak chcete vyššie rozlíšenie, prevráťte telefón"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rozloženie skladacieho zariadenia"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Prevrátenie skladacieho zariadenia"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Predná obrazovka je zapnutá"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zložené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostupnosť"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prispôsobenie klávesových skratiek"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Stlačením klávesa priraďte skratku"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prehľadávať skratky"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žiadne výsledky vyhľadávania"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string> @@ -1431,14 +1441,21 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Presúvadlo"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavenia klávesnice"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastaviť skratku"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušiť"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stlačte kláves"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinácia klávesov sa už používa. Skúste iný kláves."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pohybujte sa v systéme pomocou klávesnice"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte sa klávesové skratky"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pohybujte sa v systéme pomocou touchpadu"</string> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučte sa gestá touchpadu"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Prechádzajte pomocou klávesnice a touchpadu"</string> + <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Pohybujte sa v systéme pomocou klávesnice a touchpadu"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte sa gestá touchpadu, klávesové skratky a ďalšie funkcie"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Prechod späť"</string> <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Prejsť na plochu"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 38f1e5a1fb6d..4704e980cf72 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pripomočki na zaklenjenem zaslonu"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Če želite aplikacijo odpreti s pripomočkom, morate potrditi, da ste to vi. Upoštevajte tudi, da si jih lahko ogledajo vsi, tudi ko je tablični računalnik zaklenjen. Nekateri pripomočki morda niso predvideni za uporabo na zaklenjenem zaslonu, zato jih tukaj morda ni varno dodati."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumem"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Prikaz v obliki oblačka na vrhu razdelka z obvestili za pogovor in kot profilna slika na zaklenjenem zaslonu, preglasitev načina Ne moti."</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prednostno"</string> <string name="no_shortcut" msgid="8257177117568230126">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podpira pogovornih funkcij."</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Za ta obvestila ni mogoče spremeniti nastavitev."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Obvestil o klicih ni mogoče spreminjati."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Te skupine obvestil ni mogoče konfigurirati tukaj"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Uporabite manj kot <xliff:g id="LENGTH">%1$d</xliff:g> znakov."</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"kopiranje v odložišče."</string> <string name="basic_status" msgid="2315371112182658176">"Odprt pogovor"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Pripomočki za pogovore"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Dotaknite se pogovora, da ga dodate na začetni zaslon."</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Za višjo ločljivost obrnite telefon"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Razpiranje zložljive naprave"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Obračanje zložljive naprave"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Sprednji zaslon je vklopljen"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zaprto"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"razprto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostopnost"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagajanje bližnjičnih tipk"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pritisnite tipko za dodelitev bližnjice"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Iskanje po bližnjicah"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ni rezultatov iskanja"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ročica za vlečenje"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavitve tipkovnice"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavite bližnjico"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Prekliči"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipko"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinacija tipk je že v uporabi. Poskusite z drugo tipko."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krmarjenje s tipkovnico"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Učenje bližnjičnih tipk"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krmarjenje s sledilno ploščico"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 9ed8bf29d630..d082bfdc1e56 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikacionet në ekranin e kyçjes"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes, shfaqet si flluskë dhe ndërpret modalitetin \"Mos shqetëso\""</string> <string name="notification_priority_title" msgid="2079708866333537093">"Me përparësi"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mbështet veçoritë e bisedës"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Këto njoftime nuk mund të modifikohen."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Njoftimet e telefonatave nuk mund të modifikohen."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ky grup njoftimesh nuk mund të konfigurohet këtu"</string> @@ -1418,7 +1424,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizo shkurtoret e tastierës"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Shtyp tastin për të caktuar shkurtoren"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Asnjë rezultat kërkimi"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string> @@ -1431,9 +1442,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Doreza e zvarritjes"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cilësimet e tastierës"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Cakto shkurtoren"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulo"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Shtyp tastin"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Kombinimi i tasteve është tashmë në përdorim. Provo një tast tjetër."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigo duke përdorur tastierën tënde"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Mëso shkurtoret e tastierës"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigo duke përdorur bllokun me prekje"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 102c452fc54a..71fd8879edcf 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети за закључани екран"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Да бисте отворили апликацију која користи виџет, треба да потврдите да сте то ви. Имајте у виду да свако може да га види, чак и када је таблет закључан. Неки виџети можда нису намењени за закључани екран и можда није безбедно да их тамо додате."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Важи"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Приказује се у врху обавештења о конверзацијама и као слика профила на закључаном екрану, појављује се као облачић, прекида режим Не узнемиравај"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритетно"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не подржава функције конверзације"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ова обавештења не могу да се мењају."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Обавештења о позивима не могу да се мењају."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ова група обавештења не може да се конфигурише овде"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Користите мањи број знакова од <xliff:g id="LENGTH">%1$d</xliff:g>"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број верзије"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Број верзије је копиран у привремену меморију."</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"копирајте у привремену меморију."</string> <string name="basic_status" msgid="2315371112182658176">"Отворите конверзацију"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Виџети за конверзацију"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Додирните конверзацију да бисте је додали на почетни екран"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"За већу резолуцију обрните телефон"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Уређај на преклоп се отвара"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Уређај на преклоп се обрће"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Предњи екран је укључен"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Приступачност"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Тастерске пречице"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Прилагодите тастерске пречице"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Притисните тастер да бисте доделили пречицу"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Претражите пречице"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултата претраге"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Подешавања тастатуре"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Подеси пречицу"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притисните тастер"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Комбинација тастера се већ користи. Пробајте са другим тастером."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Крећите се помоћу тастатуре"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Сазнајте више о тастерским пречицама"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Крећите се помоћу тачпеда"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index c2ac21691aa1..175b1d0783ff 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgetar för låsskärm"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Du måste verifiera din identitet innan du öppnar en app med en widget. Tänk också på att alla kan se dem, även när surfplattan är låst. Vissa widgetar kanske inte är avsedda för låsskärmen och det kan vara osäkert att lägga till dem här."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Visas högst upp i konversationsaviseringarna och som profilbild på låsskärmen, visas som bubbla, åsidosätter Stör ej"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inte stöd för konversationsfunktioner"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Det går inte att ändra de här aviseringarna."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Det går inte att ändra samtalsaviseringarna."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Den här aviseringsgruppen kan inte konfigureras här"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Använd färre än <xliff:g id="LENGTH">%1$d</xliff:g> tecken"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"kopiera till urklipp."</string> <string name="basic_status" msgid="2315371112182658176">"Öppen konversation"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Konversationswidgetar"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Tryck på en konversation för att lägga till den på startskärmen"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Vänd telefonen för högre upplösning"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En vikbar enhet viks upp"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En vikbar enhet vänds"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Den främre skärmen har aktiverats"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"hopvikt"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"uppvikt"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Anpassa kortkommandon"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Tryck på tangenten för att ange kortkommando"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Inga sökresultat"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handtag"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tangentbordsinställningar"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ange kortkommando"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryck på tangenten"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Tangentkombinationen används redan. Testa en annan tangent."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigera med tangentbordet"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lär dig kortkommandon"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigera med styrplattan"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 3418907ea44c..fe431b7cfecc 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Utahitaji kuthibitisha kuwa ni wewe ili ufungue programu ukitumia wijeti. Pia, kumbuka kuwa mtu yeyote anaweza kuziona, hata kishikwambi chako kikiwa kimefungwa. Huenda baadhi ya wijeti hazikukusudiwa kutumika kwenye skrini yako iliyofungwa na huenda si salama kuziweka hapa."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Nimeelewa"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Huonyeshwa kwenye sehemu ya juu ya arifa za mazungumzo na kama picha ya wasifu kwenye skrini iliyofungwa. Huonekana kama kiputo na hukatiza kipengele cha Usinisumbue"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Kipaumbele"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> haitumii vipengele vya mazungumzo"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Arifa hizi haziwezi kubadilishwa."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Arifa za simu haziwezi kubadilishwa."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Kikundi hiki cha arifa hakiwezi kuwekewa mipangilio hapa"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Kwa ubora wa juu, geuza simu"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Kifaa kinachokunjwa kikikunjuliwa"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Kifaa kinachokunjwa kikigeuzwa"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Umewasha skrini ya mbele"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kimekunjwa"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kimefunguliwa"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Weka mapendeleo ya mikato ya kibodi"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Bonyeza kitufe ukabidhi njia ya mkato"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hamna matokeo ya utafutaji"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string> @@ -1431,15 +1441,22 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mipangilio ya Kibodi"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Weka njia ya mkato"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Acha"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Bonyeza kitufe"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Tayari unatumia mchanganyiko huu wa vitufe. Jatibu kitufe kingine."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Kusogeza kwa kutumia kibodi yako"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Fahamu kuhusu mikato ya kibodi"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Kusogeza kwa kutumia padi yako ya kugusa"</string> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Fahamu miguso ya padi ya kugusa"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Kusogeza kwa kutumia kibodi na padi yako ya kugusa"</string> - <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Jifunze kuhusu miguso ya padi ya kugusa, mikato ya kibodi na mengineyo"</string> + <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Fahamu kuhusu miguso ya padi ya kugusa, mikato ya kibodi na mengineyo"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Rudi nyuma"</string> <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Nenda kwenye ukurasa wa mwanzo"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Angalia programu za hivi majuzi"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 99c912ca072a..6d03b79dfebf 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"பூட்டுத் திரை விட்ஜெட்கள்"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"உரையாடல் அறிவிப்புகளின் மேற்பகுதியில் காட்டப்படும், திரை பூட்டப்பட்டிருக்கும்போது சுயவிவரப் படமாகக் காட்டப்படும், குமிழாகத் தோன்றும், தொந்தரவு செய்ய வேண்டாம் அம்சம் இயக்கப்பட்டிருக்கும்போதும் காட்டப்படும்"</string> <string name="notification_priority_title" msgid="2079708866333537093">"முன்னுரிமை"</string> <string name="no_shortcut" msgid="8257177117568230126">"உரையாடல் அம்சங்களை <xliff:g id="APP_NAME">%1$s</xliff:g> ஆதரிக்காது"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"இந்த அறிவிப்புகளை மாற்ற இயலாது."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"அழைப்பு அறிவிப்புகளை மாற்ற முடியாது."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"இந்த அறிவுப்புக் குழுக்களை இங்கே உள்ளமைக்க இயலாது"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"உயர் தெளிவுத்திறனுக்கு, மொபைலை ஃபிளிப் செய்யுங்கள்"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"மடக்கத்தக்க சாதனம் திறக்கப்படுகிறது"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"மடக்கத்தக்க சாதனம் ஃபிளிப் செய்யப்பட்டு திருப்பப்படுகிறது"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"முன்பக்கத் திரை இயக்கப்பட்டுள்ளது"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"மடக்கப்பட்டது"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"விரிக்கப்பட்டது"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"மாற்றுத்திறன் வசதி"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"கீபோர்டு ஷார்ட்கட்களைப் பிரத்தியேகப்படுத்துதல்"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"ஷார்ட்கட்டை அமைக்க பட்டனை அழுத்துங்கள்"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ஷார்ட்கட்களைத் தேடுக"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"தேடல் முடிவுகள் இல்லை"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"கீபோர்டு அமைப்புகள்"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ஷார்ட்கட்டை அமையுங்கள்"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ரத்துசெய்"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"பட்டனை அழுத்துங்கள்"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"பட்டன் சேர்க்கை ஏற்கெனவே பயன்பாட்டில் உள்ளது. வேறொரு பட்டனைப் பயன்படுத்திப் பார்க்கவும்."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"கீபோர்டு ஷார்ட்கட்கள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"டச்பேடைப் பயன்படுத்திச் செல்லுதல்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 5a088231c945..cfe5101572c0 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"లాక్ స్క్రీన్ విడ్జెట్లు"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"విడ్జెట్ను ఉపయోగించి యాప్ను తెరవడానికి, ఇది మీరేనని వెరిఫై చేయాల్సి ఉంటుంది. అలాగే, మీ టాబ్లెట్ లాక్ చేసి ఉన్నప్పటికీ, ఎవరైనా వాటిని చూడగలరని గుర్తుంచుకోండి. కొన్ని విడ్జెట్లు మీ లాక్ స్క్రీన్కు తగినవి కాకపోవచ్చు, వాటిని ఇక్కడ జోడించడం సురక్షితం కాకపోవచ్చు."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"అర్థమైంది"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్డౌన్ మెనూ"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"సంభాషణ నోటిఫికేషన్ల ఎగువున, లాక్ స్క్రీన్లో ప్రొఫైల్ ఫోటోగా చూపిస్తుంది, బబుల్గా కనిపిస్తుంది, \'అంతరాయం కలిగించవద్దు\'ను అంతరాయం కలిగిస్తుంది"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ప్రాధాన్యత"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> సంభాషణ ఫీచర్లను సపోర్ట్ చేయదు"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ఈ నోటిఫికేషన్లను ఎడిట్ చేయడం వీలుపడదు."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"కాల్ నోటిఫికేషన్లను ఎడిట్ చేయడం సాధ్యం కాదు."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"ఈ నోటిఫికేషన్ల గ్రూప్ను ఇక్కడ కాన్ఫిగర్ చేయలేము"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"అధిక రిజల్యూషన్ కోసం, ఫోన్ను తిప్పండి"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"మడవగల పరికరం విప్పబడుతోంది"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"మడవగల పరికరం చుట్టూ తిప్పబడుతోంది"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"ముందు వైపు స్క్రీన్ ఆన్ అయింది"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"మడిచే సదుపాయం గల పరికరం"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"మడిచే సదుపాయం లేని పరికరం"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"యాక్సెసిబిలిటీ"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"కీబోర్డ్ షార్ట్కట్లు"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"కీబోర్డ్ షార్ట్కట్లను అనుకూలంగా మార్చండి"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"షార్ట్కట్ను కేటాయించడానికి కీని నొక్కండి"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"షార్ట్కట్లను వెతకండి"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"సెర్చ్ ఫలితాలు ఏవీ లేవు"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"కీబోర్డ్ సెట్టింగ్లు"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"షార్ట్కట్ను సెట్ చేయండి"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"రద్దు చేయండి"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"కీని నొక్కండి"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"కీ కాంబినేషన్ ఇప్పటికే వినియోగంలో ఉంది. వేరొక కీని ట్రై చేయండి."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"మీ కీబోర్డ్ ఉపయోగించి నావిగేట్ చేయండి"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"కీబోర్డ్ షార్ట్కట్ల గురించి తెలుసుకోండి"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"మీ టచ్ప్యాడ్ను ఉపయోగించి నావిగేట్ చేయండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 816514a9797f..d2b7012985cf 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"วิดเจ็ตในหน้าจอล็อก"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"หากต้องการเปิดแอปโดยใช้วิดเจ็ต คุณจะต้องยืนยันตัวตนของคุณ นอกจากนี้ โปรดทราบว่าผู้อื่นจะดูวิดเจ็ตเหล่านี้ได้แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม วิดเจ็ตบางอย่างอาจไม่ได้มีไว้สำหรับหน้าจอล็อกของคุณ และอาจไม่ปลอดภัยที่จะเพิ่มที่นี่"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"รับทราบ"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"แสดงที่ด้านบนของการแจ้งเตือนการสนทนาและเป็นรูปโปรไฟล์บนหน้าจอล็อก ปรากฏเป็นบับเบิล แสดงในโหมดห้ามรบกวน"</string> <string name="notification_priority_title" msgid="2079708866333537093">"สำคัญ"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่รองรับฟีเจอร์การสนทนา"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"แก้ไขการแจ้งเตือนเหล่านี้ไม่ได้"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"แก้ไขการแจ้งเตือนสายเรียกเข้าไม่ได้"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"การแจ้งเตือนกลุ่มนี้กำหนดค่าที่นี่ไม่ได้"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"ใช้อักขระไม่เกิน <xliff:g id="LENGTH">%1$d</xliff:g> ตัว"</string> <string name="build_number_clip_data_label" msgid="3623176728412560914">"หมายเลขบิลด์"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"คัดลอกหมายเลขบิลด์ไปยังคลิปบอร์ดแล้ว"</string> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"คัดลอกไปยังคลิปบอร์ด"</string> <string name="basic_status" msgid="2315371112182658176">"เปิดการสนทนา"</string> <string name="select_conversation_title" msgid="6716364118095089519">"วิดเจ็ตการสนทนา"</string> <string name="select_conversation_text" msgid="3376048251434956013">"แตะการสนทนาเพื่อเพิ่มไปยังหน้าจอหลัก"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"พลิกด้านโทรศัพท์เพื่อให้ได้ภาพที่มีความละเอียดมากขึ้น"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"อุปกรณ์ที่พับได้กำลังกางออก"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"อุปกรณ์ที่พับได้กำลังพลิกไปมา"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"เปิดหน้าจอด้านหน้าแล้ว"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"พับ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"กางออก"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"การช่วยเหลือพิเศษ"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"แป้นพิมพ์ลัด"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ปรับแต่งแป้นพิมพ์ลัด"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"กดแป้นเพื่อกำหนดแป้นพิมพ์ลัด"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ไม่พบผลการค้นหา"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"การตั้งค่าแป้นพิมพ์"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ตั้งค่าแป้นพิมพ์ลัด"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ยกเลิก"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"กดแป้น"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"มีการใช้แป้นที่กดร่วมกันนี้แล้ว โปรดลองใช้แป้นอื่น"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ดูข้อมูลเกี่ยวกับแป้นพิมพ์ลัด"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ไปยังส่วนต่างๆ โดยใช้ทัชแพด"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 3845fcd314a4..132bb62199a6 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Mga widget ng lock screen"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para magbukas ng app gamit ang isang widget, kakailanganin mong i-verify na ikaw iyan. Bukod pa rito, tandaang puwedeng tingnan ng kahit na sino ang mga ito, kahit na naka-lock ang iyong tablet. Posibleng hindi para sa iyong lock screen ang ilang widget at posibleng hindi ligtas ang mga ito na idagdag dito."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Makikita sa itaas ng mga notification ng pag-uusap at bilang larawan sa profile sa lock screen, lumalabas bilang bubble, naaabala ang Huwag Istorbohin"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priyoridad"</string> <string name="no_shortcut" msgid="8257177117568230126">"Hindi sinusuportahan ng <xliff:g id="APP_NAME">%1$s</xliff:g> ang mga feature ng pag-uusap"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Hindi puwedeng baguhin ang mga notification na ito."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Hindi mabago ang mga notification ng tawag."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Hindi mako-configure dito ang pangkat na ito ng mga notification"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Gumamit ng mas kaunti sa <xliff:g id="LENGTH">%1$d</xliff:g> (na) character"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"kopyahin sa clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Buksan ang pag-uusap"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Mga widget ng pag-uusap"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Mag-tap sa isang pag-uusap para idagdag ito sa iyong Home screen"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Para sa mas mataas na resolution, i-flip ang telepono"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ina-unfold na foldable na device"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Fini-flip na foldable na device"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Na-on ang screen sa harap"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"naka-fold"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"hindi naka-fold"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"I-customize ang mga keyboard shortcut"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Pindutin ang key para magtalaga ng shortcut"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Walang resulta ng paghahanap"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handle sa pag-drag"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mga Setting ng Keyboard"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Magtakda ng shortcut"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselahin"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pindutin ang key"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Ginagamit na ang kumbinasyon ng key. Sumubok ng ibang key."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Mag-navigate gamit ang iyong keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Matuto ng mga keyboard shortcut"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Mag-navigate gamit ang iyong touchpad"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 45d8b4605c29..afdc9a0a6fa8 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -113,7 +113,7 @@ <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir uygulamayı kaydet"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tüm ekranı kaydet"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Tüm ekranı kaydet: %s"</string> - <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Tüm ekranınızı kaydettiğinizde ekranınızda gösterilen her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string> + <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Ekranın tamamını kaydederken ekranınızda gösterilen her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string> <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Bir uygulamayı kaydettiğinizde o uygulamada gösterilen veya oynatılan her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string> <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı kaydet"</string> <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Kaydedilecek uygulamayı seçin"</string> @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilit ekranı widget\'ları"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Widget kullanarak bir uygulamayı açmak için kimliğinizi doğrulamanız gerekir. Ayrıca, tabletiniz kilitliyken bile widget\'ların herkes tarafından görüntülenebileceğini unutmayın. Bazı widget\'lar kilit ekranınız için tasarlanmamış olabileceğinden buraya eklenmeleri güvenli olmayabilir."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Görüşme bildirimlerinin üstünde ve kilit ekranında profil resmi olarak gösterilir, baloncuk olarak görünür, Rahatsız Etmeyin\'i kesintiye uğratır"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Öncelikli"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>, sohbet özelliklerini desteklemiyor"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Bu bildirimler değiştirilemez."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Arama bildirimleri değiştirilemez."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Bu bildirim grubu burada yapılandırılamaz"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Daha yüksek çözünürlük için telefonu çevirin"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Katlanabilir cihaz açılıyor"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Katlanabilir cihaz döndürülüyor"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Ön ekran açıldı"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"katlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"katlanmamış"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klavye kısayollarını özelleştirin"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Kısayol atamak için tuşa basın"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Arama sonucu yok"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klavye Ayarları"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Kısayol ayarla"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"İptal"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tuşa basın"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Tuş kombinasyonu zaten kullanılıyor. Başka bir tuş deneyin."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klavyenizi kullanarak gezinin"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klavye kısayollarını öğrenin"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Dokunmatik alanınızı kullanarak gezinin"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 4c4c80ea31a3..17d2081bc6f2 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджети для заблокованого екрана"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"З’являється вгорі сповіщень про розмови і як зображення профілю на заблокованому екрані, відображається як спливаючий чат, перериває режим \"Не турбувати\""</string> <string name="notification_priority_title" msgid="2079708866333537093">"Пріоритет"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не підтримує функції розмов"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ці сповіщення не можна змінити."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Сповіщення про виклик не можна змінити."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Цю групу сповіщень не можна налаштувати тут"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Для вищої роздільної здатності переверніть телефон"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Розкладний пристрій у розкладеному стані"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Розкладний пристрій обертається"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Передній екран увімкнено"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складений"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"розкладений"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Доступність"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Налаштуйте комбінації клавіш"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Натисніть клавішу, щоб призначити комбінацію клавіш"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нічого не знайдено"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер переміщення"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налаштування клавіатури"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Налаштувати комбінацію клавіш"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасувати"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натисніть клавішу"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Комбінація клавіш уже використовується. Спробуйте іншу клавішу."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігація за допомогою клавіатури"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Дізнайтеся більше про комбінації клавіш"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігація за допомогою сенсорної панелі"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 109f43eb5632..f9d41416acc6 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"مقفل اسکرین کے ویجیٹس"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ویجیٹ کے ذریعے ایپ کھولنے کے لیے آپ کو تصدیق کرنی ہوگی کہ یہ آپ ہی ہیں۔ نیز، ذہن میں رکھیں کہ کوئی بھی انہیں دیکھ سکتا ہے، یہاں تک کہ جب آپ کا ٹیبلیٹ مقفل ہو۔ ہو سکتا ہے کچھ ویجٹس آپ کی لاک اسکرین کے لیے نہ بنائے گئے ہوں اور یہاں شامل کرنا غیر محفوظ ہو سکتا ہے۔"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"سمجھ آ گئی"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"یہ گفتگو کی اطلاعات کے اوپری حصّے پر اور مقفل اسکرین پر پروفائل کی تصویر کے بطور دکھائی دیتا ہے، بلبلے کے بطور ظاہر ہوتا ہے، \'ڈسٹرب نہ کریں\' میں مداخلت کرتا ہے"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ترجیح"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ گفتگو کی خصوصیات کو سپورٹ نہیں کرتی ہے"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"ان اطلاعات کی ترمیم نہیں کی جا سکتی۔"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"کال کی اطلاعات میں ترمیم نہیں کی جا سکتی۔"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"اطلاعات کے اس گروپ کو یہاں کنفیگر نہیں کیا جا سکتا"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"زیادہ ریزولوشن کے لیے، فون پلٹائیں"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"فولڈ ہونے والے آلے کو کھولا جا رہا ہے"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"فولڈ ہونے والے آلے کو گھمایا جا رہا ہے"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"فرنٹ اسکرین آن ہے"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"فولڈ کردہ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"اَن فولڈ کردہ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ایکسیسبیلٹی"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"کی بورڈ شارٹ کٹس"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"کی بورڈ شارٹ کٹس کو حسب ضرورت بنائیں"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"شارٹ کٹ تفویض کرنے کے لیے کلید کو دبائیں"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"تلاش کا کوئی نتیجہ نہیں ہے"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"کی بورڈ کی ترتیبات"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"شارٹ کٹ سیٹ کریں"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"منسوخ کریں"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید کو دبائیں"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"کلیدی مجموعہ پہلے سے استعمال میں ہے۔ دوسری کلید آزمائیں۔"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"اپنے کی بورڈ کا استعمال کر کے نیویگیٹ کریں"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"کی بورڈ شارٹ کٹس جانیں"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"اپنے ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 1c27a0055cce..4f97000b7b1c 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Ekran qulfi vidjetlari"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ilovani vidjet orqali ochish uchun shaxsingizni tasdiqlashingiz kerak. Shuningdek, planshet qulflanganda ham bu axborotlar hammaga koʻrinishini unutmang. Ayrim vidjetlar ekran qulfiga moslanmagan va ularni bu yerda chiqarish xavfli boʻlishi mumkin."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Suhbat bildirishnomalari tepasida va ekran qulfida profil rasmi sifatida chiqariladi, bulutcha sifatida chiqadi, Bezovta qilinmasin rejimini bekor qiladi"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Muhim"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasida suhbat funksiyalari ishlamaydi"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Bu bildirishnomalarni tahrirlash imkonsiz."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Chaqiruv bildirishnomalarini tahrirlash imkonsiz."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ushbu bildirishnomalar guruhi bu yerda sozlanmaydi"</string> @@ -1220,8 +1226,7 @@ <string name="media_output_broadcast_edit_hint_no_more_than_max" msgid="3923625800037673922">"Kiritiladigan belgilar <xliff:g id="LENGTH">%1$d</xliff:g> tadan oshmasin"</string> <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> - <!-- no translation found for copy_to_clipboard_a11y_action (4312789069718446749) --> - <skip /> + <string name="copy_to_clipboard_a11y_action" msgid="4312789069718446749">"vaqtincha xotiraga nusxalash"</string> <string name="basic_status" msgid="2315371112182658176">"Suhbatni ochish"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Suhbat vidjetlari"</string> <string name="select_conversation_text" msgid="3376048251434956013">"Bosh ekranga chiqariladigan suhbat ustiga bosing"</string> @@ -1357,8 +1362,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Yuqori aniqlik uchun telefonni aylantiring"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Buklanadigan qurilma ochilmoqda"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Buklanadigan qurilma aylantirilmoqda"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Old ekran yoqildi"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"buklangan"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"buklanmagan"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1422,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tezkor tugmalarni moslash"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Tezkor tugma sozlash uchun tugmani bosing"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hech narsa topilmadi"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string> @@ -1431,9 +1440,15 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Surish dastagi"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura sozlamalari"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tezkor tugma sozlash"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Bekor qilish"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tugmani bosing"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Bu tugmalar birikmasi band. Boshqasini ishlating."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviatura yordamida kezing"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tezkor tugmalar haqida"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Sensorli panel yordamida kezing"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index d8e3bc0c1ffd..417f49415b9c 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Tiện ích trên màn hình khoá"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Tôi hiểu"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Hiện ở đầu phần thông báo cuộc trò chuyện và ở dạng ảnh hồ sơ trên màn hình khóa, xuất hiện ở dạng bong bóng, làm gián đoạn chế độ Không làm phiền"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Mức độ ưu tiên"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> không hỗ trợ các tính năng trò chuyện"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Không thể sửa đổi các thông báo này."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Không thể sửa đổi các thông báo cuộc gọi."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Không thể định cấu hình nhóm thông báo này tại đây"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Để có độ phân giải cao hơn, hãy lật điện thoại"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Thiết bị có thể gập lại đang được mở ra"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Thiết bị có thể gập lại đang được lật ngược"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Đã bật màn hình trước"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gập"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"mở"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hỗ trợ tiếp cận"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tuỳ chỉnh phím tắt"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Nhấn phím để chỉ định phím tắt"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm lối tắt"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Không có kết quả tìm kiếm nào"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Nút kéo"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cài đặt bàn phím"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Đặt phím tắt"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Huỷ"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nhấn phím"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Tổ hợp phím đã được sử dụng. Hãy thử một phím khác."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Di chuyển bằng bàn phím"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tìm hiểu về phím tắt"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Di chuyển bằng bàn di chuột"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index d9a974aad94a..3b72bd50f867 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -113,7 +113,7 @@ <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"录制整个屏幕"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"全屏录制:%s"</string> - <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string> + <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款详情、消息、照片、音频、视频等敏感信息。"</string> <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"录制单个应用时,该应用中显示或播放的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string> <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"录制屏幕"</string> <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"选择要录制的应用"</string> @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"锁屏微件"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"以气泡形式显示在对话通知顶部(屏幕锁定时显示为个人资料照片),并且会中断勿扰模式"</string> <string name="notification_priority_title" msgid="2079708866333537093">"优先"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>不支持对话功能"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"无法修改这些通知。"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"无法修改来电通知。"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"您无法在此处配置这组通知"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"若要获得更高的分辨率,请翻转手机"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展开可折叠设备"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻转可折叠设备"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"前屏已开启"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折叠状态"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"展开状态"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"无障碍功能"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自定义键盘快捷键"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"按下按键即可指定快捷键"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"无搜索结果"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"键盘设置"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"设置快捷键"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按键"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"按键组合已被使用,请尝试使用其他按键。"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用键盘导航"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"了解键盘快捷键"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用触控板导航"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 443b8556fe18..aca9e685847e 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"上鎖畫面小工具"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"以對話氣泡形式顯示在對話通知頂部 (在上鎖畫面會顯示為個人檔案相片),並會中斷「請勿打擾」模式"</string> <string name="notification_priority_title" msgid="2079708866333537093">"優先"</string> <string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"無法修改這些通知。"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"無法修改通話通知。"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"無法在此設定這組通知"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"如要提高解像度,請切換至手機後置鏡頭"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展開折疊式裝置"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻轉折疊式裝置"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"正面螢幕已開啟"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已打開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙功能"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"按鍵即可指派快速鍵"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"沒有相符的搜尋結果"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按鍵"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"此按鍵組合已在使用,請改用其他按鍵。"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤導覽"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"瞭解鍵盤快速鍵"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板導覽"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 4b84f25fca1b..506ed573949b 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"螢幕鎖定小工具"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會刪除。"</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"以對話框的形式顯示在對話通知頂端 (螢幕鎖定時會顯示為個人資料相片),並會中斷「零打擾」模式"</string> <string name="notification_priority_title" msgid="2079708866333537093">"優先"</string> <string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"無法修改這些通知。"</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"無法修改來電通知。"</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"無法在這裡設定這個通知群組"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"如要提高解析度,請切換至手機後置鏡頭"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展開的折疊式裝置"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻轉折疊式裝置"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"正面螢幕已開啟"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已展開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"按下按鍵即可指派快速鍵"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"找不到相符的搜尋結果"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按鍵"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"這個按鍵組合已在使用中,請改用其他按鍵。"</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤操作"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"學習鍵盤快速鍵"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板操作"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 4afb67d2af8f..1a17fcc4a35d 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -528,6 +528,10 @@ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Amawijethi wesikrini esikhiyiwe"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ukuze uvule i-app usebenzisa iwijethi, uzodinga ukuqinisekisa ukuthi nguwe. Futhi, khumbula ukuthi noma ubani angakwazi ukuzibuka, nanoma ithebhulethi yakho ikhiyiwe. Amanye amawijethi kungenzeka abengahloselwe ukukhiya isikrini sakho futhi kungenzeka awaphephile ukuthi angafakwa lapha."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ngiyezwa"</string> + <!-- no translation found for glanceable_hub_lockscreen_affordance_label (1461611028615752141) --> + <skip /> + <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (511359420883794513) --> + <skip /> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string> @@ -780,6 +784,8 @@ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Ivela phezu kwezaziso zengxoxo futhi njengesithombe sephrofayela esikrinini sokukhiya, ivela njengebhamuza, ukuphazamisa okuthi Ungaphazamisi"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Okubalulekile"</string> <string name="no_shortcut" msgid="8257177117568230126">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayisekeli izici zengxoxo"</string> + <!-- no translation found for notification_guts_bundle_feedback (5393570876655201459) --> + <skip /> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Lezi zaziso azikwazi ukushintshwa."</string> <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Izaziso zekholi azikwazi ukushintshwa."</string> <string name="notification_multichannel_desc" msgid="7414593090056236179">"Leli qembu lezaziso alikwazi ukulungiselelwa lapha"</string> @@ -1357,8 +1363,7 @@ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Ukuze uthole ukulungiswa okuphezulu, phendula ifoni"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Idivayisi egoqekayo iyembulwa"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Idivayisi egoqekayo iphendulwa nxazonke"</string> - <!-- no translation found for rear_display_unfolded_front_screen_on (5946436677205643170) --> - <skip /> + <string name="rear_display_unfolded_front_screen_on" msgid="5946436677205643170">"Isikrini sangaphambili sivuliwe"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kugoqiwe"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kuvuliwe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> @@ -1418,7 +1423,12 @@ <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string> <string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string> <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Hlela izinqamuleli zekhibhodi ngendlela oyifisayo"</string> - <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Cindezela ukhiye ukuze unikeze isinqamuleli"</string> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_dialog_title (7106420484940737208) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_add_shortcut_description (6866025005347407696) --> + <skip /> + <!-- no translation found for shortcut_customize_mode_remove_shortcut_description (6851287900585057128) --> + <skip /> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ayikho imiphumela yosesho"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string> @@ -1431,9 +1441,16 @@ <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Hudula isibambi"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Amasethingi Ekhibhodi"</string> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setha isinqamuleli"</string> + <!-- no translation found for shortcut_helper_customize_dialog_remove_button_label (6546386970440176552) --> + <skip /> <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Khansela"</string> <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Cindezela ukhiye"</string> - <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Inhlanganisela yokhiye isiyasetshenziswa kakade. Zama omunye ukhiye."</string> + <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (7693234470526626327) --> + <skip /> + <!-- no translation found for shortcut_customizer_generic_error_message (3128454624049722741) --> + <skip /> + <!-- no translation found for shortcut_helper_plus_symbol (4534843157353732011) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Funa usebenzisa ikhibhodi yakho"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Funda izinqamuleli zamakhibhodi"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Funa usebenzisa iphedi yokuthinta"</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 411f36b73802..52704030108f 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1995,12 +1995,16 @@ <dimen name="backlight_indicator_step_large_radius">28dp</dimen> <!-- Touchpad gestures tutorial--> - <!-- This value is in unit of dp/ms + <!-- This value is in dp/ms. TriggerSwipeUpTouchTracker (which is base for gesture tutorial implementation) uses value of 0.5dp but from manual testing it's too high and doesn't really feel like it's forcing slowing down. Also for tutorial it should be fine to lean to the side of being more strict rather than not strict enough and not teaching user the proper gesture as a result.--> <dimen name="touchpad_recent_apps_gesture_velocity_threshold">0.05dp</dimen> + <!-- This value is in dp/ms. + As above, it's not tied to system-wide value (defined in launcher's + quickstep_fling_threshold_speed) because for tutorial it's fine to be more strict. --> + <dimen name="touchpad_home_gesture_velocity_threshold">0.5dp</dimen> <!-- Normal gesture threshold is system_gestures_distance_threshold but for tutorial we can exaggerate gesture, which also works much better with live tracking --> <dimen name="touchpad_tutorial_gestures_distance_threshold">48dp</dimen> @@ -2086,7 +2090,7 @@ <dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen> <dimen name="volume_dialog_floating_sliders_vertical_padding_negative">-10dp</dimen> <dimen name="volume_dialog_floating_sliders_horizontal_padding">4dp</dimen> - <dimen name="volume_dialog_button_size">48dp</dimen> + <dimen name="volume_dialog_button_size">40dp</dimen> <dimen name="volume_dialog_slider_width">52dp</dimen> <dimen name="volume_dialog_slider_height">254dp</dimen> @@ -2094,7 +2098,7 @@ <dimen name="volume_dialog_background_square_corner_radius">12dp</dimen> - <dimen name="volume_dialog_ringer_drawer_button_size">40dp</dimen> + <dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen> <dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen> <dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 713aa47970ee..4bf67a12963a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -753,7 +753,7 @@ <!-- QuickSettings: Bluetooth dialog device in audio sharing default summary [CHAR LIMIT=50]--> <string name="quick_settings_bluetooth_device_audio_sharing">Audio Sharing</string> <!-- QuickSettings: Bluetooth dialog device summary for devices that are capable of audio sharing and switching to active[CHAR LIMIT=NONE]--> - <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active">Tap to switch or share audio</string> + <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active">Supports audio sharing</string> <!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]--> <string name="quick_settings_bluetooth_device_saved">Saved</string> <!-- QuickSettings: Accessibility label to disconnect a device [CHAR LIMIT=NONE]--> @@ -2287,6 +2287,14 @@ <string name="system_multitasking_replace">During split screen: replace an app from one to another</string> <!-- User visible title for the keyboard shortcut that moves a focused task to a next display [CHAR LIMIT=70] --> <string name="system_multitasking_move_to_next_display">Move active window between displays</string> + <!-- User visible title for the keyboard shortcut that snaps a task to the left in desktop mode [CHAR LIMIT=70] --> + <string name="system_desktop_mode_snap_left_window">Move window to the left</string> + <!-- User visible title for the keyboard shortcut that snaps a task to the right in desktop mode [CHAR LIMIT=70] --> + <string name="system_desktop_mode_snap_right_window">Move window to the right</string> + <!-- User visible title for the keyboard shortcut that toggles between maximizing and restoring a task's previous bounds in desktop mode [CHAR LIMIT=70] --> + <string name="system_desktop_mode_toggle_maximize_window">Maximize window</string> + <!-- User visible title for the keyboard shortcut that minimizes a task in desktop mode [CHAR LIMIT=70] --> + <string name="system_desktop_mode_minimize_window">Minimize window</string> <!-- User visible title for the input keyboard shortcuts list. [CHAR LIMIT=70] --> <string name="keyboard_shortcut_group_input">Input</string> @@ -3816,6 +3824,14 @@ [CHAR LIMIT=NONE] --> <string name="shortcut_helper_key_combinations_or_separator">or</string> + <!-- Word that combines different possible key combinations of a shortcut. For example the + "Go to home screen" shortcut could be triggered using "ctrl" plus "h". + This is only used for accessibility content descriptions of the key combinations. + [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_key_combinations_and_conjunction">plus</string> + <!-- Word that represents the forward slash character "/". To be used only in accessibility + content descriptions. [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_key_combinations_forward_slash">forward slash</string> <!-- Content description of the drag handle that allows to swipe to dismiss the shortcut helper. The helper is a component that shows the user which keyboard shortcuts they can use. The helper shows shortcuts in categories, which can be collapsed or expanded. diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 0381811e29ed..9aa71374fb8e 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1084,11 +1084,6 @@ <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen --> <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item> <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> - - <!-- - TODO(b/309578419): Make the activity handle insets properly and then remove this. - --> - <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> <style name="Widget.SliceView.VolumePanel"> diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java index b116e297ccbf..dcbacec5b630 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static com.android.systemui.Flags.simPinUseSlotId; import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ACTIVE_DATA_SUB_CHANGED; import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ON_TELEPHONY_CAPABLE; import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_REFRESH_CARRIER_INFO; @@ -368,10 +369,12 @@ public class CarrierTextManager { for (int i = 0; i < numSubs; i++) { int subId = subs.get(i).getSubscriptionId(); + int slotId = subs.get(i).getSimSlotIndex(); carrierNames[i] = ""; subsIds[i] = subId; - subOrderBySlot[subs.get(i).getSimSlotIndex()] = i; - int simState = mKeyguardUpdateMonitor.getSimState(subId); + subOrderBySlot[slotId] = i; + int simState = simPinUseSlotId() ? mKeyguardUpdateMonitor.getSimStateForSlotId(slotId) + : mKeyguardUpdateMonitor.getSimState(subId); CharSequence carrierName = subs.get(i).getCarrierName(); CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName); mLogger.logUpdateLoopStart(subId, simState, String.valueOf(carrierName)); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 8ca0e807b31c..2c8fff83a821 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -44,6 +44,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.systemui.Flags.simPinBouncerReset; +import static com.android.systemui.Flags.simPinUseSlotId; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED; import android.annotation.AnyThread; @@ -172,7 +173,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -318,6 +318,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final Object mSimDataLockObject = new Object(); HashMap<Integer, SimData> mSimDatas = new HashMap<>(); + HashMap<Integer, SimData> mSimDatasBySlotId = new HashMap<>(); HashMap<Integer, ServiceState> mServiceStates = new HashMap<>(); private int mPhoneState; @@ -616,16 +617,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // It is possible for active subscriptions to become invalid (-1), and these will // not be present in the subscriptionInfo list synchronized (mSimDataLockObject) { - Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator(); + var iter = simPinUseSlotId() ? mSimDatasBySlotId.entrySet().iterator() + : mSimDatas.entrySet().iterator(); + while (iter.hasNext()) { - Map.Entry<Integer, SimData> simData = iter.next(); - if (!activeSubIds.contains(simData.getKey())) { - mSimLogger.logInvalidSubId(simData.getKey()); + SimData data = iter.next().getValue(); + if (!activeSubIds.contains(data.subId)) { + mSimLogger.logInvalidSubId(data.subId, data.slotId); iter.remove(); - SimData data = simData.getValue(); for (int j = 0; j < mCallbacks.size(); j++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get(); + var cb = mCallbacks.get(j).get(); if (cb != null) { cb.onSimStateChanged(data.subId, data.slotId, data.simState); } @@ -634,10 +636,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } for (int i = 0; i < changedSubscriptions.size(); i++) { - SimData data = mSimDatas.get( - changedSubscriptions.get(i).getSubscriptionId()); + SimData data; + if (simPinUseSlotId()) { + data = mSimDatasBySlotId.get(changedSubscriptions.get(i) + .getSimSlotIndex()); + } else { + data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId()); + } for (int j = 0; j < mCallbacks.size(); j++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get(); + var cb = mCallbacks.get(j).get(); if (cb != null) { cb.onSimStateChanged(data.subId, data.slotId, data.simState); } @@ -3409,12 +3416,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ private void invalidateSlot(int slotId) { synchronized (mSimDataLockObject) { - Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator(); + var iter = simPinUseSlotId() ? mSimDatasBySlotId.entrySet().iterator() + : mSimDatas.entrySet().iterator(); while (iter.hasNext()) { SimData data = iter.next().getValue(); if (data.slotId == slotId && SubscriptionManager.isValidSubscriptionId(data.subId)) { - mSimLogger.logInvalidSubId(data.subId); + mSimLogger.logInvalidSubId(data.subId, data.slotId); iter.remove(); } } @@ -3427,7 +3435,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting void handleSimStateChange(int subId, int slotId, int state) { Assert.isMainThread(); - mSimLogger.logSimState(subId, slotId, state); + mSimLogger.logSimState(subId, slotId, TelephonyManager.simStateToString(state)); boolean becameAbsent = ABSENT_SIM_STATE_LIST.contains(state); if (!SubscriptionManager.isValidSubscriptionId(subId)) { @@ -3444,11 +3452,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // TODO(b/327476182): Preserve SIM_STATE_CARD_IO_ERROR sims in a separate data source. synchronized (mSimDataLockObject) { - SimData data = mSimDatas.get(subId); + SimData data = simPinUseSlotId() ? mSimDatasBySlotId.get(slotId) : mSimDatas.get(subId); final boolean changed; if (data == null) { data = new SimData(state, slotId, subId); - mSimDatas.put(subId, data); + if (simPinUseSlotId()) { + mSimDatasBySlotId.put(slotId, data); + } else { + mSimDatas.put(subId, data); + } changed = true; // no data yet; force update } else { changed = (data.simState != state || data.subId != subId || data.slotId != slotId); @@ -3727,7 +3739,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab callback.onTelephonyCapable(mTelephonyCapable); synchronized (mSimDataLockObject) { - for (Entry<Integer, SimData> data : mSimDatas.entrySet()) { + var simDatas = simPinUseSlotId() ? mSimDatasBySlotId : mSimDatas; + for (Entry<Integer, SimData> data : simDatas.entrySet()) { final SimData state = data.getValue(); callback.onSimStateChanged(state.subId, state.slotId, state.simState); } @@ -3860,7 +3873,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ public boolean isSimPinSecure() { synchronized (mSimDataLockObject) { - for (SimData data : mSimDatas.values()) { + var simDatas = simPinUseSlotId() ? mSimDatasBySlotId : mSimDatas; + for (SimData data : simDatas.values()) { if (isSimPinSecure(data.simState)) { return true; } @@ -3870,6 +3884,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } public int getSimState(int subId) { + if (simPinUseSlotId()) { + throw new UnsupportedOperationException("Method not supported with flag " + + "simPinUseSlotId"); + } synchronized (mSimDataLockObject) { if (mSimDatas.containsKey(subId)) { return mSimDatas.get(subId).simState; @@ -3879,12 +3897,32 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + /** + * Find the sim state for a slot id, or SIM_STATE_UNKNOWN if not found. + */ + public int getSimStateForSlotId(int slotId) { + if (!simPinUseSlotId()) { + throw new UnsupportedOperationException("Method not supported without flag " + + "simPinUseSlotId"); + } + synchronized (mSimDataLockObject) { + if (mSimDatasBySlotId.containsKey(slotId)) { + return mSimDatasBySlotId.get(slotId).simState; + } else { + return TelephonyManager.SIM_STATE_UNKNOWN; + } + } + } + private int getSlotId(int subId) { synchronized (mSimDataLockObject) { - if (!mSimDatas.containsKey(subId)) { - refreshSimState(subId, SubscriptionManager.getSlotIndex(subId)); + var simDatas = simPinUseSlotId() ? mSimDatasBySlotId : mSimDatas; + int slotId = SubscriptionManager.getSlotIndex(subId); + int index = simPinUseSlotId() ? slotId : subId; + if (!simDatas.containsKey(index)) { + refreshSimState(subId, slotId); } - SimData simData = mSimDatas.get(subId); + SimData simData = simDatas.get(index); return simData != null ? simData.slotId : SubscriptionManager.INVALID_SUBSCRIPTION_ID; } } @@ -3928,8 +3966,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean refreshSimState(int subId, int slotId) { int state = mTelephonyManager.getSimState(slotId); synchronized (mSimDataLockObject) { - SimData data = mSimDatas.get(subId); - + SimData data = simPinUseSlotId() ? mSimDatasBySlotId.get(slotId) : mSimDatas.get(subId); if (!SubscriptionManager.isValidSubscriptionId(subId)) { invalidateSlot(slotId); } @@ -3937,7 +3974,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean changed; if (data == null) { data = new SimData(state, slotId, subId); - mSimDatas.put(subId, data); + if (simPinUseSlotId()) { + mSimDatasBySlotId.put(slotId, data); + } else { + mSimDatas.put(subId, data); + } changed = true; // no data yet; force update } else { changed = data.simState != state; @@ -4033,10 +4074,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < list.size(); i++) { final SubscriptionInfo info = list.get(i); final int id = info.getSubscriptionId(); - int slotId = getSlotId(id); - if (state == getSimState(id) && bestSlotId > slotId) { - resultId = id; - bestSlotId = slotId; + final int slotId = info.getSimSlotIndex(); + if (simPinUseSlotId()) { + if (state == getSimStateForSlotId(slotId) && bestSlotId > slotId) { + resultId = id; + bestSlotId = slotId; + } + } else { + if (state == getSimState(id) && bestSlotId > slotId) { + resultId = id; + bestSlotId = slotId; + } } } return resultId; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java index 9513c8e4d8be..5a9cbce73e4b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java @@ -174,9 +174,20 @@ public interface KeyguardViewController { /** * Stop showing the alternate bouncer, if showing. + * + * <p>Should be like calling {@link #hideAlternateBouncer(boolean, boolean)} with a {@code true} + * {@code clearDismissAction} parameter. */ void hideAlternateBouncer(boolean updateScrim); + /** + * Stop showing the alternate bouncer, if showing. + * + * @param updateScrim Whether to update the scrim + * @param clearDismissAction Whether the pending dismiss action should be cleared + */ + void hideAlternateBouncer(boolean updateScrim, boolean clearDismissAction); + // TODO: Deprecate registerStatusBar in KeyguardViewController interface. It is currently // only used for testing purposes in StatusBarKeyguardViewManager, and it prevents us from // achieving complete abstraction away from where the Keyguard View is mounted. diff --git a/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt index a81698ba3387..fb26c6a00c27 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt @@ -47,12 +47,15 @@ class SimLogger @Inject constructor(@SimLog private val logBuffer: LogBuffer) { fun log(@CompileTimeConstant msg: String, level: LogLevel) = logBuffer.log(TAG, level, msg) - fun logInvalidSubId(subId: Int) { + fun logInvalidSubId(subId: Int, slotId: Int) { logBuffer.log( TAG, INFO, - { int1 = subId }, - { "Previously active sub id $int1 is now invalid, will remove" }, + { + int1 = subId + int2 = slotId + }, + { "Previously active subId: $int1, slotId: $int2 is now invalid, will remove" }, ) } @@ -94,16 +97,16 @@ class SimLogger @Inject constructor(@SimLog private val logBuffer: LogBuffer) { ) } - fun logSimState(subId: Int, slotId: Int, state: Int) { + fun logSimState(subId: Int, slotId: Int, state: String) { logBuffer.log( TAG, DEBUG, { int1 = subId int2 = slotId - long1 = state.toLong() + str1 = state }, - { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" }, + { "handleSimStateChange(subId=$int1, slotId=$int2, state=$str1)" }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 7d5cf232bcb9..08d3e17c03d7 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -283,7 +283,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold com.android.internal.R.integer.config_shortAnimTime)); updateDimensions(); - final Size windowFrameSize = restoreMagnificationWindowFrameSizeIfPossible(); + final Size windowFrameSize = restoreMagnificationWindowFrameIndexAndSizeIfPossible(); setMagnificationFrame(windowFrameSize.getWidth(), windowFrameSize.getHeight(), mWindowBounds.width() / 2, mWindowBounds.height() / 2); computeBounceAnimationScale(); @@ -541,7 +541,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return false; } mWindowBounds.set(currentWindowBounds); - final Size windowFrameSize = restoreMagnificationWindowFrameSizeIfPossible(); + final Size windowFrameSize = restoreMagnificationWindowFrameIndexAndSizeIfPossible(); final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width(); final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height(); @@ -787,18 +787,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mMagnificationFrame.set(initX, initY, initX + width, initY + height); } - private Size restoreMagnificationWindowFrameSizeIfPossible() { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - return restoreMagnificationWindowFrameIndexAndSizeIfPossible(); - } - - if (!mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity()) { - return getDefaultMagnificationWindowFrameSize(); - } - - return mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity(); - } - private Size restoreMagnificationWindowFrameIndexAndSizeIfPossible() { if (!mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity()) { notifyWindowSizeRestored(MagnificationSize.DEFAULT); @@ -845,9 +833,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } // Set the surface of the SurfaceView to black to avoid users seeing the contents below the // magnifier when the mirrored surface has an alpha less than 1. - if (Flags.addBlackBackgroundForWindowMagnifier()) { - mTransaction.setColor(mMirrorSurfaceView.getSurfaceControl(), COLOR_BLACK_ARRAY); - } + mTransaction.setColor(mMirrorSurfaceView.getSurfaceControl(), COLOR_BLACK_ARRAY); mTransaction.show(mMirrorSurface) .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl()); modifyWindowMagnification(false); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java index ee36c6e8ad35..558c87c456ef 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java @@ -22,8 +22,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Size; -import com.android.systemui.Flags; - /** * Class to handle SharedPreference for window magnification size. */ @@ -52,14 +50,10 @@ final class WindowMagnificationFrameSizePrefs { * Saves the window frame size for current screen density. */ public void saveIndexAndSizeForCurrentDensity(int index, Size size) { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - mWindowMagnificationSizePreferences.edit() - .putString(getKey(), - WindowMagnificationFrameSpec.serialize(index, size)).apply(); - } else { - mWindowMagnificationSizePreferences.edit() - .putString(getKey(), size.toString()).apply(); - } + mWindowMagnificationSizePreferences + .edit() + .putString(getKey(), WindowMagnificationFrameSpec.serialize(index, size)) + .apply(); } /** @@ -91,13 +85,9 @@ final class WindowMagnificationFrameSizePrefs { * Gets the size preference for current screen density. */ public Size getSizeForCurrentDensity() { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - return WindowMagnificationFrameSpec - .deserialize(mWindowMagnificationSizePreferences.getString(getKey(), null)) - .getSize(); - } else { - return Size.parseSize(mWindowMagnificationSizePreferences.getString(getKey(), null)); - } + return WindowMagnificationFrameSpec.deserialize( + mWindowMagnificationSizePreferences.getString(getKey(), null)) + .getSize(); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index 2f0ca6e6bf9d..9d9f5691816e 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -59,7 +59,6 @@ import android.widget.Switch; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.systemui.Flags; import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView; import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; @@ -460,12 +459,8 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest mAllowDiagonalScrollingView.setVisibility(View.VISIBLE); mFullScreenButton.setVisibility(View.GONE); if (selectedButtonIndex == MagnificationSize.FULLSCREEN) { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - selectedButtonIndex = - windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); - } else { - selectedButtonIndex = MagnificationSize.CUSTOM; - } + selectedButtonIndex = + windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); } break; @@ -482,10 +477,8 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } else { // mode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW mEditButton.setVisibility(View.VISIBLE); mAllowDiagonalScrollingView.setVisibility(View.VISIBLE); - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - selectedButtonIndex = - windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); - } + selectedButtonIndex = + windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); } break; @@ -581,13 +574,15 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest || (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0 || (configDiff & ActivityInfo.CONFIG_FONT_SCALE) != 0 || (configDiff & ActivityInfo.CONFIG_LOCALE) != 0 - || (configDiff & ActivityInfo.CONFIG_DENSITY) != 0) { + || (configDiff & ActivityInfo.CONFIG_DENSITY) != 0 + || (configDiff & ActivityInfo.CONFIG_FONT_WEIGHT_ADJUSTMENT) != 0) { // We listen to following config changes to trigger layout inflation: // CONFIG_UI_MODE: theme change // CONFIG_ASSETS_PATHS: wallpaper change // CONFIG_FONT_SCALE: font size change // CONFIG_LOCALE: language change // CONFIG_DENSITY: display size change + // CONFIG_FONT_WEIGHT_ADJUSTMENT: bold text setting change mParams.width = getPanelWidth(mContext); mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/activity/ActivityManagerModule.kt b/packages/SystemUI/src/com/android/systemui/activity/ActivityManagerModule.kt new file mode 100644 index 000000000000..db315e4e0bf7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/activity/ActivityManagerModule.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.activity + +import com.android.systemui.activity.data.repository.ActivityManagerRepository +import com.android.systemui.activity.data.repository.ActivityManagerRepositoryImpl +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module + +@Module +interface ActivityManagerModule { + @Binds + @SysUISingleton + fun activityManagerRepository(impl: ActivityManagerRepositoryImpl): ActivityManagerRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/activity/data/repository/ActivityManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/activity/data/repository/ActivityManagerRepository.kt new file mode 100644 index 000000000000..94614b70beda --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/activity/data/repository/ActivityManagerRepository.kt @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.activity.data.repository + +import android.app.ActivityManager +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.log.core.Logger +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart + +/** Repository for interfacing with [ActivityManager]. */ +interface ActivityManagerRepository { + /** + * Given a UID, creates a flow that emits true when the process with the given UID is visible to + * the user and false otherwise. + * + * @param identifyingLogTag a tag identifying who created this flow, used for logging. + */ + fun createIsAppVisibleFlow( + creationUid: Int, + logger: Logger, + identifyingLogTag: String, + ): Flow<Boolean> +} + +@SysUISingleton +class ActivityManagerRepositoryImpl +@Inject +constructor( + @Background private val backgroundContext: CoroutineContext, + private val activityManager: ActivityManager, +) : ActivityManagerRepository { + override fun createIsAppVisibleFlow( + creationUid: Int, + logger: Logger, + identifyingLogTag: String, + ): Flow<Boolean> { + return conflatedCallbackFlow { + val listener = + object : ActivityManager.OnUidImportanceListener { + override fun onUidImportance(uid: Int, importance: Int) { + if (uid != creationUid) { + return + } + val isAppVisible = isAppVisibleToUser(importance) + logger.d({ + "$str1: #onUidImportance. importance=$int1, isAppVisible=$bool1" + }) { + str1 = identifyingLogTag + int1 = importance + bool1 = isAppVisible + } + trySend(isAppVisible) + } + } + try { + // TODO(b/286258140): Replace this with the #addOnUidImportanceListener + // overload that filters to certain UIDs. + activityManager.addOnUidImportanceListener(listener, IMPORTANCE_CUTPOINT) + } catch (e: SecurityException) { + logger.e({ "$str1: Security exception on #addOnUidImportanceListener" }, e) { + str1 = identifyingLogTag + } + } + + awaitClose { activityManager.removeOnUidImportanceListener(listener) } + } + .distinctUntilChanged() + .onStart { + try { + val isVisibleOnStart = + isAppVisibleToUser(activityManager.getUidImportance(creationUid)) + logger.d({ "$str1: Starting UID observation. isAppVisible=$bool1" }) { + str1 = identifyingLogTag + bool1 = isVisibleOnStart + } + emit(isVisibleOnStart) + } catch (e: SecurityException) { + logger.e({ "$str1: Security exception on #getUidImportance" }, e) { + str1 = identifyingLogTag + } + emit(false) + } + } + .flowOn(backgroundContext) + } + + /** Returns true if the given [importance] represents an app that's visible to the user. */ + private fun isAppVisibleToUser(importance: Int): Boolean { + return importance <= IMPORTANCE_CUTPOINT + } + + companion object { + private const val IMPORTANCE_CUTPOINT = IMPORTANCE_FOREGROUND + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b491c94db151..f6b6655dca4d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -64,6 +64,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.biometrics.AuthController.ScaleFactorProvider; import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; +import com.android.systemui.biometrics.plugins.AuthContextPlugins; import com.android.systemui.biometrics.shared.model.BiometricModalities; import com.android.systemui.biometrics.shared.model.PromptKind; import com.android.systemui.biometrics.ui.CredentialView; @@ -132,6 +133,7 @@ public class AuthContainerView extends LinearLayout private final int mEffectiveUserId; private final IBinder mWindowToken = new Binder(); private final ViewCaptureAwareWindowManager mWindowManager; + @Nullable private final AuthContextPlugins mAuthContextPlugins; private final Interpolator mLinearOutSlowIn; private final LockPatternUtils mLockPatternUtils; private final WakefulnessLifecycle mWakefulnessLifecycle; @@ -289,6 +291,7 @@ public class AuthContainerView extends LinearLayout @Nullable List<FaceSensorPropertiesInternal> faceProps, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, + @Nullable AuthContextPlugins authContextPlugins, @NonNull LockPatternUtils lockPatternUtils, @NonNull InteractionJankMonitor jankMonitor, @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider, @@ -306,6 +309,7 @@ public class AuthContainerView extends LinearLayout WindowManager wm = getContext().getSystemService(WindowManager.class); mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture, enableViewCaptureTracing()); + mAuthContextPlugins = authContextPlugins; mWakefulnessLifecycle = wakefulnessLifecycle; mApplicationCoroutineScope = applicationCoroutineScope; @@ -446,7 +450,7 @@ public class AuthContainerView extends LinearLayout final CredentialViewModel vm = mCredentialViewModelProvider.get(); vm.setAnimateContents(animateContents); ((CredentialView) mCredentialView).init(vm, this, mPanelController, animatePanel, - mBiometricCallback); + mBiometricCallback, mAuthContextPlugins); mLayout.addView(mCredentialView); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index a5bd559dcbf2..4faf6ff9f596 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -21,6 +21,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR; import static android.view.Display.INVALID_DISPLAY; +import static com.android.systemui.Flags.contAuthPlugin; import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; import android.annotation.NonNull; @@ -74,6 +75,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.CoreStartable; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; +import com.android.systemui.biometrics.plugins.AuthContextPlugins; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; @@ -108,6 +110,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.inject.Inject; @@ -139,6 +142,7 @@ public class AuthController implements private final ActivityTaskManager mActivityTaskManager; @Nullable private final FingerprintManager mFingerprintManager; @Nullable private final FaceManager mFaceManager; + @Nullable private final AuthContextPlugins mContextPlugins; private final Provider<UdfpsController> mUdfpsControllerFactory; private final CoroutineScope mApplicationCoroutineScope; private Job mBiometricContextListenerJob = null; @@ -717,6 +721,7 @@ public class AuthController implements @NonNull WindowManager windowManager, @Nullable FingerprintManager fingerprintManager, @Nullable FaceManager faceManager, + Optional<AuthContextPlugins> contextPlugins, Provider<UdfpsController> udfpsControllerFactory, @NonNull DisplayManager displayManager, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @@ -744,6 +749,7 @@ public class AuthController implements mActivityTaskManager = activityTaskManager; mFingerprintManager = fingerprintManager; mFaceManager = faceManager; + mContextPlugins = contAuthPlugin() ? contextPlugins.orElse(null) : null; mUdfpsControllerFactory = udfpsControllerFactory; mUdfpsLogger = udfpsLogger; mDisplayManager = displayManager; @@ -858,6 +864,10 @@ public class AuthController implements mActivityTaskManager.registerTaskStackListener(mTaskStackListener); mOrientationListener.enable(); updateSensorLocations(); + + if (mContextPlugins != null) { + mContextPlugins.activate(); + } } @Override @@ -1350,7 +1360,7 @@ public class AuthController implements config.mSensorIds = sensorIds; config.mScaleProvider = this::getScaleFactor; return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps, - wakefulnessLifecycle, userManager, lockPatternUtils, + wakefulnessLifecycle, userManager, mContextPlugins, lockPatternUtils, mInteractionJankMonitor, mPromptSelectorInteractor, viewModel, mCredentialViewModelProvider, bgExecutor, mVibratorHelper, mLazyViewCapture, mMSDLPlayer); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt index e2a8a691b1fd..60ce17721b42 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt @@ -36,6 +36,7 @@ import com.android.systemui.biometrics.data.repository.FingerprintPropertyReposi import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl import com.android.systemui.biometrics.data.repository.PromptRepository import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl +import com.android.systemui.biometrics.plugins.AuthContextPlugins import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector import com.android.systemui.biometrics.udfps.EllipseOverlapDetector import com.android.systemui.biometrics.udfps.OverlapDetector @@ -58,7 +59,7 @@ import javax.inject.Qualifier /** Dagger module for all things biometric. */ @Module interface BiometricsModule { - /** Starts AuthController. */ + /** Starts AuthController. */ @Binds @IntoMap @ClassKey(AuthController::class) @@ -103,8 +104,9 @@ interface BiometricsModule { @SysUISingleton fun displayStateRepository(impl: DisplayStateRepositoryImpl): DisplayStateRepository - @BindsOptionalOf - fun deviceEntryUnlockTrackerViewBinder(): DeviceEntryUnlockTrackerViewBinder + @BindsOptionalOf fun authContextPlugins(): AuthContextPlugins + + @BindsOptionalOf fun deviceEntryUnlockTrackerViewBinder(): DeviceEntryUnlockTrackerViewBinder companion object { /** Background [Executor] for HAL related operations. */ @@ -117,8 +119,7 @@ interface BiometricsModule { @Provides fun providesUdfpsUtils(): UdfpsUtils = UdfpsUtils() - @Provides - fun provideIconProvider(context: Context): IconProvider = IconProvider(context) + @Provides fun provideIconProvider(context: Context): IconProvider = IconProvider(context) @Provides @SysUISingleton @@ -136,7 +137,7 @@ interface BiometricsModule { EllipseOverlapDetectorParams( minOverlap = values[3], targetSize = values[2], - stepSize = values[4].toInt() + stepSize = values[4].toInt(), ) ) } else { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/plugins/AuthContextPlugins.kt b/packages/SystemUI/src/com/android/systemui/biometrics/plugins/AuthContextPlugins.kt new file mode 100644 index 000000000000..ca38e9869ed1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/plugins/AuthContextPlugins.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.plugins + +import com.android.systemui.plugins.AuthContextPlugin +import com.android.systemui.plugins.PluginManager + +/** Wrapper interface for registering & forwarding events to all available [AuthContextPlugin]s. */ +interface AuthContextPlugins { + /** Finds and actives all plugins via SysUI's [PluginManager] (should be called at startup). */ + fun activate() + + /** + * Interact with all registered plugins. + * + * The provided [block] will be repeated for each available plugin. + */ + suspend fun use(block: (AuthContextPlugin) -> Unit) + + /** + * Like [use] but when no existing coroutine context is available. + * + * The [block] will be run on SysUI's general background context and can, optionally, be + * confined to [runOnMain] (defaults to a background thread). + */ + fun useInBackground(runOnMain: Boolean = false, block: (AuthContextPlugin) -> Unit) +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt index b28733f5cc55..dad140f00cee 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt @@ -11,6 +11,7 @@ import android.view.accessibility.AccessibilityManager import android.widget.LinearLayout import android.widget.TextView import com.android.systemui.biometrics.AuthPanelController +import com.android.systemui.biometrics.plugins.AuthContextPlugins import com.android.systemui.biometrics.ui.binder.CredentialViewBinder import com.android.systemui.biometrics.ui.binder.Spaghetti import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel @@ -33,6 +34,7 @@ class CredentialPasswordView(context: Context, attrs: AttributeSet?) : panelViewController: AuthPanelController, animatePanel: Boolean, legacyCallback: Spaghetti.Callback, + plugins: AuthContextPlugins?, ) { CredentialViewBinder.bind( this, @@ -40,7 +42,8 @@ class CredentialPasswordView(context: Context, attrs: AttributeSet?) : viewModel, panelViewController, animatePanel, - legacyCallback + legacyCallback, + plugins, ) } @@ -78,7 +81,7 @@ class CredentialPasswordView(context: Context, attrs: AttributeSet?) : 0, statusBarInsets.top, 0, - if (keyboardInsets.bottom == 0) navigationInsets.bottom else keyboardInsets.bottom + if (keyboardInsets.bottom == 0) navigationInsets.bottom else keyboardInsets.bottom, ) return WindowInsets.CONSUMED } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt index d9d286fe7035..e80a79ba1641 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt @@ -8,6 +8,7 @@ import android.view.WindowInsets import android.view.WindowInsets.Type import android.widget.LinearLayout import com.android.systemui.biometrics.AuthPanelController +import com.android.systemui.biometrics.plugins.AuthContextPlugins import com.android.systemui.biometrics.ui.binder.CredentialViewBinder import com.android.systemui.biometrics.ui.binder.Spaghetti import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel @@ -23,6 +24,7 @@ class CredentialPatternView(context: Context, attrs: AttributeSet?) : panelViewController: AuthPanelController, animatePanel: Boolean, legacyCallback: Spaghetti.Callback, + plugins: AuthContextPlugins?, ) { CredentialViewBinder.bind( this, @@ -30,7 +32,8 @@ class CredentialPatternView(context: Context, attrs: AttributeSet?) : viewModel, panelViewController, animatePanel, - legacyCallback + legacyCallback, + plugins, ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt index e2f98958ab55..f3e49175538f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt @@ -1,6 +1,7 @@ package com.android.systemui.biometrics.ui import com.android.systemui.biometrics.AuthPanelController +import com.android.systemui.biometrics.plugins.AuthContextPlugins import com.android.systemui.biometrics.ui.binder.Spaghetti import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel @@ -29,5 +30,6 @@ sealed interface CredentialView { panelViewController: AuthPanelController, animatePanel: Boolean, legacyCallback: Spaghetti.Callback, + plugins: AuthContextPlugins?, ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt index 39543e78f784..10b12117a3a9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt @@ -9,18 +9,21 @@ import android.widget.TextView import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.biometrics.AuthPanelController +import com.android.systemui.biometrics.plugins.AuthContextPlugins import com.android.systemui.biometrics.ui.CredentialPasswordView import com.android.systemui.biometrics.ui.CredentialPatternView import com.android.systemui.biometrics.ui.CredentialView import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.plugins.AuthContextPlugin import com.android.systemui.res.R import kotlinx.coroutines.Job +import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch private const val ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150 @@ -42,6 +45,7 @@ object CredentialViewBinder { panelViewController: AuthPanelController, animatePanel: Boolean, legacyCallback: Spaghetti.Callback, + plugins: AuthContextPlugins?, maxErrorDuration: Long = 3_000L, requestFocusForInput: Boolean = true, ) { @@ -72,6 +76,10 @@ object CredentialViewBinder { } repeatOnLifecycle(Lifecycle.State.STARTED) { + if (plugins != null) { + launch { plugins.notifyShowingBPCredential(view) } + } + // show prompt metadata launch { viewModel.header.collect { header -> @@ -136,6 +144,12 @@ object CredentialViewBinder { host.onCredentialAttemptsRemaining(info.remaining!!, info.message) } } + + try { + awaitCancellation() + } catch (_: Throwable) { + plugins?.notifyHidingBPCredential() + } } } @@ -172,3 +186,15 @@ private var TextView.textOrHide: String? text = if (gone) "" else value } get() = text?.toString() + +private suspend fun AuthContextPlugins.notifyShowingBPCredential(view: View) = use { plugin -> + plugin.onShowingSensitiveSurface( + AuthContextPlugin.SensitiveSurface.BiometricPrompt(view = view, isCredential = true) + ) +} + +private fun AuthContextPlugins.notifyHidingBPCredential() = useInBackground { plugin -> + plugin.onHidingSensitiveSurface( + AuthContextPlugin.SensitiveSurface.BiometricPrompt(isCredential = true) + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt index 13c72097c06e..4dc2a13480f5 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt @@ -27,7 +27,6 @@ import com.android.settingslib.bluetooth.HearingAidProfile import com.android.settingslib.bluetooth.LeAudioProfile import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast import com.android.settingslib.bluetooth.LocalBluetoothManager -import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -69,7 +68,7 @@ constructor( } deviceItem.type == DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { - if (audioSharingQsDialogImprovement()) { + if (audioSharingInteractor.qsDialogImprovementAvailable()) { withContext(mainDispatcher) { delegateFactory .create(deviceItem.cachedBluetoothDevice) diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt index 65f110533573..c4f26cd46bf8 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt @@ -16,10 +16,13 @@ package com.android.systemui.bluetooth.qsdialog +import android.content.Context +import androidx.annotation.WorkerThread import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager -import com.android.settingslib.bluetooth.onPlaybackStarted +import com.android.settingslib.bluetooth.onBroadcastMetadataChanged +import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import javax.inject.Inject @@ -52,6 +55,8 @@ interface AudioSharingInteractor { suspend fun startAudioSharing() suspend fun audioSharingAvailable(): Boolean + + suspend fun qsDialogImprovementAvailable(): Boolean } @SysUISingleton @@ -59,11 +64,14 @@ interface AudioSharingInteractor { class AudioSharingInteractorImpl @Inject constructor( + private val context: Context, private val localBluetoothManager: LocalBluetoothManager?, private val audioSharingRepository: AudioSharingRepository, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : AudioSharingInteractor { + private var previewEnabled: Boolean? = null + override val isAudioSharingOn: Flow<Boolean> = flow { emit(audioSharingAvailable()) } .flatMapLatest { isEnabled -> @@ -93,10 +101,10 @@ constructor( isAudioSharingOn .mapNotNull { audioSharingOn -> if (audioSharingOn) { - // onPlaybackStarted could emit multiple times during one - // audio sharing session, we only perform add source on the - // first time - profile.onPlaybackStarted.firstOrNull() + // onBroadcastMetadataChanged could emit multiple times during one + // audio sharing session, we only perform add source on the first + // time + profile.onBroadcastMetadataChanged.firstOrNull() } else { null } @@ -141,6 +149,20 @@ constructor( override suspend fun audioSharingAvailable(): Boolean { return audioSharingRepository.audioSharingAvailable() } + + override suspend fun qsDialogImprovementAvailable(): Boolean { + return withContext(backgroundDispatcher) { + audioSharingQsDialogImprovement() || isAudioSharingPreviewEnabled() + } + } + + @WorkerThread + private fun isAudioSharingPreviewEnabled(): Boolean { + if (previewEnabled == null) { + previewEnabled = BluetoothUtils.isAudioSharingPreviewEnabled(context.contentResolver) + } + return previewEnabled ?: false + } } @SysUISingleton @@ -160,4 +182,6 @@ class AudioSharingInteractorEmptyImpl @Inject constructor() : AudioSharingIntera override suspend fun startAudioSharing() {} override suspend fun audioSharingAvailable(): Boolean = false + + override suspend fun qsDialogImprovementAvailable(): Boolean = false } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt index b255a4f55220..497d8cf2e159 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt @@ -26,10 +26,10 @@ import android.view.ViewGroup import androidx.annotation.DimenRes import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast -import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement import com.android.systemui.Prefs import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator @@ -56,7 +56,6 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** ViewModel for Bluetooth Dialog after clicking on the Bluetooth QS tile. */ @@ -145,7 +144,7 @@ constructor( bluetoothDeviceMetadataInteractor.metadataUpdate, if ( audioSharingInteractor.audioSharingAvailable() && - audioSharingQsDialogImprovement() + audioSharingInteractor.qsDialogImprovementAvailable() ) { audioSharingInteractor.audioSourceStateUpdate } else { @@ -165,7 +164,7 @@ constructor( .launchIn(this) if (audioSharingInteractor.audioSharingAvailable()) { - if (audioSharingQsDialogImprovement()) { + if (audioSharingInteractor.qsDialogImprovementAvailable()) { launch { audioSharingInteractor.handleAudioSourceWhenReady() } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt index 8b5fde384837..afe9a1eec0b6 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt @@ -55,7 +55,10 @@ interface AudioSharingModule { settingsLibAudioSharingRepository: SettingsLibAudioSharingRepository, @Background backgroundDispatcher: CoroutineDispatcher, ): AudioSharingRepository = - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { AudioSharingRepositoryImpl( localBluetoothManager, settingsLibAudioSharingRepository, @@ -72,7 +75,10 @@ interface AudioSharingModule { impl: Lazy<AudioSharingInteractorImpl>, emptyImpl: Lazy<AudioSharingInteractorEmptyImpl>, ): AudioSharingInteractor = - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { impl.get() } else { emptyImpl.get() @@ -85,7 +91,10 @@ interface AudioSharingModule { audioSharingImpl: Lazy<AudioSharingDeviceItemActionInteractorImpl>, impl: Lazy<DeviceItemActionInteractorImpl>, ): DeviceItemActionInteractor = - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { audioSharingImpl.get() } else { impl.get() @@ -97,7 +106,10 @@ interface AudioSharingModule { localBluetoothManager: LocalBluetoothManager? ): List<DeviceItemFactory> = buildList { add(ActiveMediaDeviceItemFactory()) - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { add(AudioSharingMediaDeviceItemFactory(localBluetoothManager)) add(AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)) } @@ -112,7 +124,10 @@ interface AudioSharingModule { localBluetoothManager: LocalBluetoothManager? ): List<DeviceItemType> = buildList { add(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE) - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { add(DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE) add(DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt index 12f06bbd4f5e..8a4cc63f65fb 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt @@ -3,6 +3,8 @@ package com.android.systemui.bouncer.ui.binder import android.view.ViewGroup import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.dagger.KeyguardBouncerComponent +import com.android.systemui.Flags.contAuthPlugin +import com.android.systemui.biometrics.plugins.AuthContextPlugins import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags @@ -17,6 +19,7 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition import com.android.systemui.log.BouncerLogger import com.android.systemui.user.domain.interactor.SelectedUserInteractor import dagger.Lazy +import java.util.Optional import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -60,6 +63,7 @@ class BouncerViewBinder constructor( private val legacyBouncerDependencies: Lazy<LegacyBouncerDependencies>, private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>, + private val contextPlugins: Optional<AuthContextPlugins>, ) { fun bind(view: ViewGroup) { if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) { @@ -85,6 +89,7 @@ constructor( deps.bouncerMessageInteractor, deps.bouncerLogger, deps.selectedUserInteractor, + if (contAuthPlugin()) contextPlugins.orElse(null) else null, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt index 71eda0c19e6f..434a9ce58c3b 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt @@ -22,11 +22,13 @@ import android.view.ViewGroup import android.window.OnBackAnimationCallback import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.KeyguardSecurityContainerController import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityView import com.android.keyguard.dagger.KeyguardBouncerComponent +import com.android.systemui.biometrics.plugins.AuthContextPlugins import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.bouncer.ui.BouncerViewDelegate @@ -35,10 +37,10 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.log.BouncerLogger import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.plugins.AuthContextPlugin import com.android.systemui.user.domain.interactor.SelectedUserInteractor import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.filter -import com.android.app.tracing.coroutines.launchTraced as launch /** Binds the bouncer container to its view model. */ object KeyguardBouncerViewBinder { @@ -52,6 +54,7 @@ object KeyguardBouncerViewBinder { bouncerMessageInteractor: BouncerMessageInteractor, bouncerLogger: BouncerLogger, selectedUserInteractor: SelectedUserInteractor, + plugins: AuthContextPlugins?, ) { // Builds the KeyguardSecurityContainerController from bouncer view group. val securityContainerController: KeyguardSecurityContainerController = @@ -94,7 +97,7 @@ object KeyguardBouncerViewBinder { override fun setDismissAction( onDismissAction: ActivityStarter.OnDismissAction?, - cancelAction: Runnable? + cancelAction: Runnable?, ) { securityContainerController.setOnDismissAction(onDismissAction, cancelAction) } @@ -138,7 +141,7 @@ object KeyguardBouncerViewBinder { it.bindMessageView( bouncerMessageInteractor, messageAreaControllerFactory, - bouncerLogger + bouncerLogger, ) } } else { @@ -149,6 +152,13 @@ object KeyguardBouncerViewBinder { securityContainerController.reset() securityContainerController.onPause() } + plugins?.apply { + if (isShowing) { + notifyBouncerShowing(view) + } else { + notifyBouncerGone() + } + } } } @@ -209,7 +219,7 @@ object KeyguardBouncerViewBinder { securityContainerController.showMessage( it.message, it.colorStateList, - /* animated= */ true + /* animated= */ true, ) viewModel.onMessageShown() } @@ -233,8 +243,19 @@ object KeyguardBouncerViewBinder { awaitCancellation() } finally { viewModel.setBouncerViewDelegate(null) + plugins?.notifyBouncerGone() } } } } } + +private suspend fun AuthContextPlugins.notifyBouncerShowing(view: View) = use { plugin -> + plugin.onShowingSensitiveSurface( + AuthContextPlugin.SensitiveSurface.LockscreenBouncer(view = view) + ) +} + +private fun AuthContextPlugins.notifyBouncerGone() = useInBackground { plugin -> + plugin.onHidingSensitiveSurface(AuthContextPlugin.SensitiveSurface.LockscreenBouncer()) +} diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt index 47d913746ed7..5deb751ced27 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt @@ -23,6 +23,7 @@ import android.graphics.Bitmap import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.input.key.type import androidx.core.graphics.drawable.toBitmap +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.app.tracing.coroutines.traceCoroutine import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel @@ -35,6 +36,7 @@ import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardMediaKeyInteractor import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel @@ -48,7 +50,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import com.android.app.tracing.coroutines.launchTraced as launch /** Models UI state for the content of the bouncer scene. */ class BouncerSceneContentViewModel @@ -67,6 +68,7 @@ constructor( private val bouncerHapticPlayer: BouncerHapticPlayer, private val keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor, private val bouncerActionButtonInteractor: BouncerActionButtonInteractor, + private val keyguardDismissActionInteractor: KeyguardDismissActionInteractor, ) : ExclusiveActivatable() { private val _selectedUserImage = MutableStateFlow<Bitmap?>(null) val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow() @@ -421,6 +423,13 @@ constructor( } } + /** + * Notifies that the bouncer UI has been destroyed (e.g. the composable left the composition). + */ + fun onUiDestroyed() { + keyguardDismissActionInteractor.clearDismissAction() + } + data class DialogViewModel( val text: String, diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt index 5bad9fc9c5d7..6f2a2c4ccaaa 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt @@ -33,6 +33,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue @@ -105,11 +106,11 @@ private fun BrightnessSlider( null } - val overriddenByAppState = + val overriddenByAppState by if (Flags.showToastWhenAppControlBrightness()) { - viewModel.brightnessOverriddenByWindow.collectAsStateWithLifecycle().value + viewModel.brightnessOverriddenByWindow.collectAsStateWithLifecycle() } else { - false + remember { mutableStateOf(false) } } PlatformSlider( diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt index 7b54815ab344..26abb48ce7db 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt @@ -20,9 +20,11 @@ import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL import android.content.IntentFilter import android.content.pm.UserInfo +import android.content.res.Resources import android.os.UserHandle import android.provider.Settings import com.android.systemui.Flags.communalHub +import com.android.systemui.Flags.glanceableHubV2 import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.communal.data.model.CommunalEnabledState import com.android.systemui.communal.data.model.DisabledReason @@ -33,6 +35,7 @@ import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_U import com.android.systemui.communal.shared.model.CommunalBackgroundType import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.util.kotlin.emitOnStart @@ -53,13 +56,30 @@ interface CommunalSettingsRepository { fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> /** - * Returns true if both the communal trunk-stable flag and resource flag are enabled. + * Returns true if any glanceable hub functionality should be enabled via configs and flags. * - * The trunk-stable flag is controlled by server rollout and is on all devices. The resource - * flag is enabled via resource overlay only on products we want the hub to be present on. + * This should be used for preventing basic glanceable hub functionality from running on devices + * that don't need it. + * + * If the glanceable_hub_v2 flag is enabled, checks the config_glanceableHubEnabled Android + * config boolean. Otherwise, checks the old config_communalServiceEnabled config and + * communal_hub flag. */ fun getFlagEnabled(): Boolean + /** + * Returns true if the Android config config_glanceableHubEnabled and the glanceable_hub_v2 flag + * are enabled. + * + * This should be used to flag off new glanceable hub or dream behavior that should launch + * together with the new hub experience that brings the hub to mobile. + * + * The trunk-stable flag is controlled by server rollout and is on all devices. The Android + * config flag is enabled via resource overlay only on products we want the hub to be present + * on. + */ + fun getV2FlagEnabled(): Boolean + /** Keyguard widgets enabled state by Device Policy Manager for the specified user. */ fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> @@ -72,6 +92,7 @@ class CommunalSettingsRepositoryImpl @Inject constructor( @Background private val bgDispatcher: CoroutineDispatcher, + @Main private val resources: Resources, private val featureFlagsClassic: FeatureFlagsClassic, private val secureSettings: SecureSettings, private val broadcastDispatcher: BroadcastDispatcher, @@ -79,7 +100,18 @@ constructor( ) : CommunalSettingsRepository { override fun getFlagEnabled(): Boolean { - return featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub() + return if (getV2FlagEnabled()) { + true + } else { + // This config (exposed as a classic feature flag) is targeted only to tablet. + // TODO(b/379181581): clean up usages of communal_hub flag + featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub() + } + } + + override fun getV2FlagEnabled(): Boolean { + return resources.getBoolean(com.android.internal.R.bool.config_glanceableHubEnabled) && + glanceableHubV2() } override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> { @@ -128,7 +160,7 @@ constructor( secureSettings.getIntForUser( GLANCEABLE_HUB_BACKGROUND_SETTING, CommunalBackgroundType.ANIMATED.value, - user.id + user.id, ) CommunalBackgroundType.entries.find { type -> type.value == intType } ?: CommunalBackgroundType.ANIMATED diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 5ecf2e6b2551..a339af3694e7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -195,7 +195,7 @@ abstract class BaseCommunalViewModel( open fun onDismissCtaTile() {} /** Called as the user starts dragging a widget to reorder. */ - open fun onReorderWidgetStart() {} + open fun onReorderWidgetStart(draggingItemKey: String) {} /** Called as the user finishes dragging a widget to reorder. */ open fun onReorderWidgetEnd() {} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 736ed5c7d336..52bf0004cbe4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -164,9 +164,8 @@ constructor( ) } - override fun onReorderWidgetStart() { - // Clear selection status - setSelectedKey(null) + override fun onReorderWidgetStart(draggingItemKey: String) { + setSelectedKey(draggingItemKey) _reorderingWidgets.value = true uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt index cc6007b400f7..50d86a24be96 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt @@ -20,6 +20,7 @@ import android.content.Context import android.os.Bundle import android.util.SizeF import com.android.app.tracing.coroutines.withContextTraced as withContext +import com.android.systemui.Flags import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate @@ -30,6 +31,9 @@ import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.dagger.qualifiers.UiBackground import dagger.Lazy import java.util.concurrent.Executor +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -53,7 +57,11 @@ constructor( withContext("$TAG#createWidget", uiBgContext) { val view = CommunalAppWidgetHostView(context, interactionHandler).apply { - setExecutor(uiBgExecutor) + if (Flags.communalHubUseThreadPoolForWidgets()) { + setExecutor(widgetExecutor) + } else { + setExecutor(uiBgExecutor) + } setAppWidget(model.appWidgetId, model.providerInfo) } @@ -90,5 +98,20 @@ constructor( private companion object { const val TAG = "WidgetViewFactory" + + val poolSize = Runtime.getRuntime().availableProcessors().coerceAtLeast(2) + + /** + * This executor is used for widget inflation. Parameters match what launcher uses. See + * [com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR]. + */ + val widgetExecutor = + ThreadPoolExecutor( + /*corePoolSize*/ poolSize, + /*maxPoolSize*/ poolSize, + /*keepAlive*/ 1, + /*unit*/ TimeUnit.SECONDS, + /*workQueue*/ LinkedBlockingQueue(), + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt index 41edb4a1ffaf..740e011b3d20 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.management +import android.app.Activity import android.app.ActivityOptions import android.content.ComponentName import android.content.Context @@ -34,25 +35,25 @@ import androidx.activity.ComponentActivity import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import com.android.systemui.res.R import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import java.util.concurrent.Executor import javax.inject.Inject -/** - * Activity for rearranging and removing controls for a given structure - */ -open class ControlsEditingActivity @Inject constructor( +/** Activity for rearranging and removing controls for a given structure */ +open class ControlsEditingActivity +@Inject +constructor( @Main private val mainExecutor: Executor, private val controller: ControlsControllerImpl, private val userTracker: UserTracker, private val customIconCache: CustomIconCache, -) : ComponentActivity() { +) : ComponentActivity(), ControlsManagementActivity { companion object { private const val DEBUG = false @@ -64,6 +65,9 @@ open class ControlsEditingActivity @Inject constructor( private val EMPTY_TEXT_ID = R.string.controls_favorite_removed } + override val activity: Activity + get() = this + private lateinit var component: ComponentName private lateinit var structure: CharSequence private lateinit var model: FavoritesModel @@ -73,16 +77,17 @@ open class ControlsEditingActivity @Inject constructor( private var isFromFavoriting: Boolean = false - private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback { - private val startingUser = controller.currentUserId + private val userTrackerCallback: UserTracker.Callback = + object : UserTracker.Callback { + private val startingUser = controller.currentUserId - override fun onUserChanged(newUser: Int, userContext: Context) { - if (newUser != startingUser) { - userTracker.removeCallback(this) - finish() + override fun onUserChanged(newUser: Int, userContext: Context) { + if (newUser != startingUser) { + userTracker.removeCallback(this) + finish() + } } } - } private val mOnBackInvokedCallback = OnBackInvokedCallback { if (DEBUG) { @@ -98,9 +103,7 @@ open class ControlsEditingActivity @Inject constructor( component = it } ?: run(this::finish) isFromFavoriting = intent.getBooleanExtra(EXTRA_FROM_FAVORITING, false) - intent.getCharSequenceExtra(EXTRA_STRUCTURE)?.let { - structure = it - } ?: run(this::finish) + intent.getCharSequenceExtra(EXTRA_STRUCTURE)?.let { structure = it } ?: run(this::finish) bindViews() @@ -117,7 +120,9 @@ open class ControlsEditingActivity @Inject constructor( Log.d(TAG, "Registered onBackInvokedCallback") } onBackInvokedDispatcher.registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback) + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + mOnBackInvokedCallback, + ) } override fun onStop() { @@ -142,18 +147,21 @@ open class ControlsEditingActivity @Inject constructor( override fun run() { finish() } - } - ).start() + }, + ) + .start() } private fun bindViews() { setContentView(R.layout.controls_management) + applyInsets(R.id.controls_management_root) + lifecycle.addObserver( ControlsAnimations.observerForAnimations( requireViewById<ViewGroup>(R.id.controls_management_root), window, - intent + intent, ) ) @@ -163,81 +171,86 @@ open class ControlsEditingActivity @Inject constructor( } requireViewById<TextView>(R.id.title).text = structure setTitle(structure) - subtitle = requireViewById<TextView>(R.id.subtitle).apply { - setText(SUBTITLE_ID) - } + subtitle = requireViewById<TextView>(R.id.subtitle).apply { setText(SUBTITLE_ID) } } private fun bindButtons() { - addControls = requireViewById<Button>(R.id.addControls).apply { - isEnabled = true - visibility = View.VISIBLE - setOnClickListener { - if (saveButton.isEnabled) { - // The user has made changes - Toast.makeText( - applicationContext, - R.string.controls_favorite_toast_no_changes, - Toast.LENGTH_SHORT - ).show() - } - if (isFromFavoriting) { - animateExitAndFinish() - } else { - startActivity(Intent(context, ControlsFavoritingActivity::class.java).also { - it.putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure) - it.putExtra(Intent.EXTRA_COMPONENT_NAME, component) - it.putExtra( - ControlsFavoritingActivity.EXTRA_APP, - intent.getCharSequenceExtra(EXTRA_APP), - ) - it.putExtra( - ControlsFavoritingActivity.EXTRA_SOURCE, - ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING, + addControls = + requireViewById<Button>(R.id.addControls).apply { + isEnabled = true + visibility = View.VISIBLE + setOnClickListener { + if (saveButton.isEnabled) { + // The user has made changes + Toast.makeText( + applicationContext, + R.string.controls_favorite_toast_no_changes, + Toast.LENGTH_SHORT, + ) + .show() + } + if (isFromFavoriting) { + animateExitAndFinish() + } else { + startActivity( + Intent(context, ControlsFavoritingActivity::class.java).also { + it.putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure) + it.putExtra(Intent.EXTRA_COMPONENT_NAME, component) + it.putExtra( + ControlsFavoritingActivity.EXTRA_APP, + intent.getCharSequenceExtra(EXTRA_APP), + ) + it.putExtra( + ControlsFavoritingActivity.EXTRA_SOURCE, + ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING, + ) + }, + ActivityOptions.makeSceneTransitionAnimation( + this@ControlsEditingActivity + ) + .toBundle(), ) - }, - ActivityOptions.makeSceneTransitionAnimation( - this@ControlsEditingActivity - ).toBundle(), - ) + } } } - } - saveButton = requireViewById<Button>(R.id.done).apply { - isEnabled = isFromFavoriting - setText(R.string.save) - setOnClickListener { - saveFavorites() - startActivity( - Intent(applicationContext, ControlsActivity::class.java), - ActivityOptions - .makeSceneTransitionAnimation(this@ControlsEditingActivity).toBundle() - ) - animateExitAndFinish() + saveButton = + requireViewById<Button>(R.id.done).apply { + isEnabled = isFromFavoriting + setText(R.string.save) + setOnClickListener { + saveFavorites() + startActivity( + Intent(applicationContext, ControlsActivity::class.java), + ActivityOptions.makeSceneTransitionAnimation(this@ControlsEditingActivity) + .toBundle(), + ) + animateExitAndFinish() + } } - } } private fun saveFavorites() { controller.replaceFavoritesForStructure( - StructureInfo(component, structure, model.favorites)) + StructureInfo(component, structure, model.favorites) + ) } - private val favoritesModelCallback = object : FavoritesModel.FavoritesModelCallback { - override fun onNoneChanged(showNoFavorites: Boolean) { - if (showNoFavorites) { - subtitle.setText(EMPTY_TEXT_ID) - } else { - subtitle.setText(SUBTITLE_ID) + private val favoritesModelCallback = + object : FavoritesModel.FavoritesModelCallback { + override fun onNoneChanged(showNoFavorites: Boolean) { + if (showNoFavorites) { + subtitle.setText(EMPTY_TEXT_ID) + } else { + subtitle.setText(SUBTITLE_ID) + } } - } - override fun onChange() = Unit + override fun onChange() = Unit - override fun onFirstChange() { - saveButton.isEnabled = true + override fun onFirstChange() { + saveButton.isEnabled = true + } } - } private fun setUpList() { val controls = controller.getFavoritesForStructure(component, structure) @@ -245,44 +258,55 @@ open class ControlsEditingActivity @Inject constructor( val elevation = resources.getFloat(R.dimen.control_card_elevation) val recyclerView = requireViewById<RecyclerView>(R.id.list) recyclerView.alpha = 0.0f - val adapter = ControlAdapter(elevation, userTracker.userId).apply { - registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - var hasAnimated = false - override fun onChanged() { - if (!hasAnimated) { - hasAnimated = true - ControlsAnimations.enterAnimation(recyclerView).start() + val adapter = + ControlAdapter(elevation, userTracker.userId).apply { + registerAdapterDataObserver( + object : RecyclerView.AdapterDataObserver() { + var hasAnimated = false + + override fun onChanged() { + if (!hasAnimated) { + hasAnimated = true + ControlsAnimations.enterAnimation(recyclerView).start() + } + } } - } - }) - } + ) + } - val margin = resources - .getDimensionPixelSize(R.dimen.controls_card_margin) + val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin) val itemDecorator = MarginItemDecorator(margin, margin) val spanCount = ControlAdapter.findMaxColumns(resources) recyclerView.apply { this.adapter = adapter - layoutManager = object : GridLayoutManager(recyclerView.context, spanCount) { - - // This will remove from the announcement the row corresponding to the divider, - // as it's not something that should be announced. - override fun getRowCountForAccessibility( - recycler: RecyclerView.Recycler, - state: RecyclerView.State - ): Int { - val initial = super.getRowCountForAccessibility(recycler, state) - return if (initial > 0) initial - 1 else initial - } - }.apply { - spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return if (adapter?.getItemViewType(position) - != ControlAdapter.TYPE_CONTROL) spanCount else 1 + layoutManager = + object : GridLayoutManager(recyclerView.context, spanCount) { + + // This will remove from the announcement the row corresponding to the + // divider, + // as it's not something that should be announced. + override fun getRowCountForAccessibility( + recycler: RecyclerView.Recycler, + state: RecyclerView.State, + ): Int { + val initial = super.getRowCountForAccessibility(recycler, state) + return if (initial > 0) initial - 1 else initial + } + } + .apply { + spanSizeLookup = + object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return if ( + adapter?.getItemViewType(position) != + ControlAdapter.TYPE_CONTROL + ) + spanCount + else 1 + } + } } - } - } addItemDecoration(itemDecorator) } adapter.changeModel(model) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index 2ea4303e2290..ab55c5326b55 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -18,6 +18,7 @@ package com.android.systemui.controls.management import android.animation.Animator import android.animation.AnimatorListenerAdapter +import android.app.Activity import android.app.ActivityOptions import android.content.ComponentName import android.content.Context @@ -39,22 +40,24 @@ import androidx.activity.ComponentActivity import androidx.annotation.VisibleForTesting import androidx.viewpager2.widget.ViewPager2 import com.android.systemui.Prefs -import com.android.systemui.res.R import com.android.systemui.controls.TooltipManager import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import java.text.Collator import java.util.concurrent.Executor import javax.inject.Inject -open class ControlsFavoritingActivity @Inject constructor( +open class ControlsFavoritingActivity +@Inject +constructor( @Main private val executor: Executor, private val controller: ControlsControllerImpl, private val userTracker: UserTracker, -) : ComponentActivity() { +) : ComponentActivity(), ControlsManagementActivity { companion object { private const val DEBUG = false @@ -74,6 +77,9 @@ open class ControlsFavoritingActivity @Inject constructor( private const val TOOLTIP_MAX_SHOWN = 2 } + override val activity: Activity + get() = this + private var component: ComponentName? = null private var appName: CharSequence? = null private var structureExtra: CharSequence? = null @@ -95,18 +101,21 @@ open class ControlsFavoritingActivity @Inject constructor( private val fromProviderSelector: Boolean get() = openSource == EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR + private val fromEditing: Boolean get() = openSource == EXTRA_SOURCE_VALUE_FROM_EDITING - private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback { - private val startingUser = controller.currentUserId - override fun onUserChanged(newUser: Int, userContext: Context) { - if (newUser != startingUser) { - userTracker.removeCallback(this) - finish() + private val userTrackerCallback: UserTracker.Callback = + object : UserTracker.Callback { + private val startingUser = controller.currentUserId + + override fun onUserChanged(newUser: Int, userContext: Context) { + if (newUser != startingUser) { + userTracker.removeCallback(this) + finish() + } } } - } private val mOnBackInvokedCallback = OnBackInvokedCallback { if (DEBUG) { @@ -138,81 +147,113 @@ open class ControlsFavoritingActivity @Inject constructor( bindViews() } - private val controlsModelCallback = object : ControlsModel.ControlsModelCallback { - override fun onFirstChange() { - doneButton.isEnabled = true - } + private val controlsModelCallback = + object : ControlsModel.ControlsModelCallback { + override fun onFirstChange() { + doneButton.isEnabled = true + } - override fun onChange() { - val structure: StructureContainer = listOfStructures[structurePager.currentItem] - rearrangeButton.isEnabled = structure.model.favorites.isNotEmpty() + override fun onChange() { + val structure: StructureContainer = listOfStructures[structurePager.currentItem] + rearrangeButton.isEnabled = structure.model.favorites.isNotEmpty() + } } - } private fun loadControls() { component?.let { componentName -> statusText.text = resources.getText(com.android.internal.R.string.loading) - val emptyZoneString = resources.getText( - R.string.controls_favorite_other_zone_header) - controller.loadForComponent(componentName, { data -> - val allControls = data.allControls - val favoriteKeys = data.favoritesIds - val error = data.errorOnLoad - val controlsByStructure = allControls.groupBy { it.control.structure ?: "" } - listOfStructures = controlsByStructure.map { - StructureContainer(it.key, AllModel( - it.value, favoriteKeys, emptyZoneString, controlsModelCallback)) - }.sortedWith(comparator) - - val structureIndex = listOfStructures.indexOfFirst { - sc -> sc.structureName == structureExtra - }.let { if (it == -1) 0 else it } - - // If we were requested to show a single structure, set the list to just that one - if (intent.getBooleanExtra(EXTRA_SINGLE_STRUCTURE, false)) { - listOfStructures = listOf(listOfStructures[structureIndex]) - } + val emptyZoneString = resources.getText(R.string.controls_favorite_other_zone_header) + controller.loadForComponent( + componentName, + { data -> + val allControls = data.allControls + val favoriteKeys = data.favoritesIds + val error = data.errorOnLoad + val controlsByStructure = allControls.groupBy { it.control.structure ?: "" } + listOfStructures = + controlsByStructure + .map { + StructureContainer( + it.key, + AllModel( + it.value, + favoriteKeys, + emptyZoneString, + controlsModelCallback, + ), + ) + } + .sortedWith(comparator) + + val structureIndex = + listOfStructures + .indexOfFirst { sc -> sc.structureName == structureExtra } + .let { if (it == -1) 0 else it } + + // If we were requested to show a single structure, set the list to just that + // one + if (intent.getBooleanExtra(EXTRA_SINGLE_STRUCTURE, false)) { + listOfStructures = listOf(listOfStructures[structureIndex]) + } - executor.execute { - structurePager.adapter = StructureAdapter(listOfStructures, userTracker.userId) - structurePager.setCurrentItem(structureIndex) - if (error) { - statusText.text = resources.getString(R.string.controls_favorite_load_error, - appName ?: "") - subtitleView.visibility = View.GONE - } else if (listOfStructures.isEmpty()) { - statusText.text = resources.getString(R.string.controls_favorite_load_none) - subtitleView.visibility = View.GONE - } else { - statusText.visibility = View.GONE - - pageIndicator.setNumPages(listOfStructures.size) - pageIndicator.setLocation(0f) - pageIndicator.visibility = - if (listOfStructures.size > 1) View.VISIBLE else View.INVISIBLE - - ControlsAnimations.enterAnimation(pageIndicator).apply { - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - // Position the tooltip if necessary after animations are complete - // so we can get the position on screen. The tooltip is not - // rooted in the layout root. - if (pageIndicator.visibility == View.VISIBLE && - mTooltipManager != null) { - val p = IntArray(2) - pageIndicator.getLocationOnScreen(p) - val x = p[0] + pageIndicator.width / 2 - val y = p[1] + pageIndicator.height - mTooltipManager?.show( - R.string.controls_structure_tooltip, x, y) - } + executor.execute { + structurePager.adapter = + StructureAdapter(listOfStructures, userTracker.userId) + structurePager.setCurrentItem(structureIndex) + if (error) { + statusText.text = + resources.getString( + R.string.controls_favorite_load_error, + appName ?: "", + ) + subtitleView.visibility = View.GONE + } else if (listOfStructures.isEmpty()) { + statusText.text = + resources.getString(R.string.controls_favorite_load_none) + subtitleView.visibility = View.GONE + } else { + statusText.visibility = View.GONE + + pageIndicator.setNumPages(listOfStructures.size) + pageIndicator.setLocation(0f) + pageIndicator.visibility = + if (listOfStructures.size > 1) View.VISIBLE else View.INVISIBLE + + ControlsAnimations.enterAnimation(pageIndicator) + .apply { + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + // Position the tooltip if necessary after + // animations are complete + // so we can get the position on screen. The tooltip + // is not + // rooted in the layout root. + if ( + pageIndicator.visibility == View.VISIBLE && + mTooltipManager != null + ) { + val p = IntArray(2) + pageIndicator.getLocationOnScreen(p) + val x = p[0] + pageIndicator.width / 2 + val y = p[1] + pageIndicator.height + mTooltipManager?.show( + R.string.controls_structure_tooltip, + x, + y, + ) + } + } + } + ) } - }) - }.start() - ControlsAnimations.enterAnimation(structurePager).start() + .start() + ControlsAnimations.enterAnimation(structurePager).start() + } } - } - }, { runnable -> cancelLoadRunnable = runnable }) + }, + { runnable -> cancelLoadRunnable = runnable }, + ) } } @@ -221,35 +262,39 @@ open class ControlsFavoritingActivity @Inject constructor( pageIndicator.alpha = 0.0f structurePager.apply { adapter = StructureAdapter(emptyList(), userTracker.userId) - registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - super.onPageSelected(position) - val name = listOfStructures[position].structureName - val title = if (!TextUtils.isEmpty(name)) name else appName - titleView.text = title - titleView.requestFocus() - } + registerOnPageChangeCallback( + object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + val name = listOfStructures[position].structureName + val title = if (!TextUtils.isEmpty(name)) name else appName + titleView.text = title + titleView.requestFocus() + } - override fun onPageScrolled( - position: Int, - positionOffset: Float, - positionOffsetPixels: Int - ) { - super.onPageScrolled(position, positionOffset, positionOffsetPixels) - pageIndicator.setLocation(position + positionOffset) + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixels: Int, + ) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels) + pageIndicator.setLocation(position + positionOffset) + } } - }) + ) } } private fun bindViews() { setContentView(R.layout.controls_management) + applyInsets(R.id.controls_management_root) + lifecycle.addObserver( ControlsAnimations.observerForAnimations( requireViewById<ViewGroup>(R.id.controls_management_root), window, - intent + intent, ) ) @@ -260,41 +305,43 @@ open class ControlsFavoritingActivity @Inject constructor( statusText = requireViewById(R.id.status_message) if (shouldShowTooltip()) { - mTooltipManager = TooltipManager(statusText.context, - TOOLTIP_PREFS_KEY, TOOLTIP_MAX_SHOWN) + mTooltipManager = + TooltipManager(statusText.context, TOOLTIP_PREFS_KEY, TOOLTIP_MAX_SHOWN) addContentView( mTooltipManager?.layout, FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, - Gravity.TOP or Gravity.LEFT - ) + Gravity.TOP or Gravity.LEFT, + ), ) } - pageIndicator = requireViewById<ManagementPageIndicator>( - R.id.structure_page_indicator).apply { - visibilityListener = { - if (it != View.VISIBLE) { - mTooltipManager?.hide(true) + pageIndicator = + requireViewById<ManagementPageIndicator>(R.id.structure_page_indicator).apply { + visibilityListener = { + if (it != View.VISIBLE) { + mTooltipManager?.hide(true) + } } } - } - val title = structureExtra - ?: (appName ?: resources.getText(R.string.controls_favorite_default_title)) - titleView = requireViewById<TextView>(R.id.title).apply { - text = title - } - subtitleView = requireViewById<TextView>(R.id.subtitle).apply { - text = resources.getText(R.string.controls_favorite_subtitle) - } + val title = + structureExtra + ?: (appName ?: resources.getText(R.string.controls_favorite_default_title)) + titleView = requireViewById<TextView>(R.id.title).apply { text = title } + subtitleView = + requireViewById<TextView>(R.id.subtitle).apply { + text = resources.getText(R.string.controls_favorite_subtitle) + } structurePager = requireViewById<ViewPager2>(R.id.structure_pager) - structurePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - super.onPageSelected(position) - mTooltipManager?.hide(true) + structurePager.registerOnPageChangeCallback( + object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + mTooltipManager?.hide(true) + } } - }) + ) bindButtons() } @@ -307,47 +354,53 @@ open class ControlsFavoritingActivity @Inject constructor( override fun run() { finish() } - } - ).start() + }, + ) + .start() } private fun bindButtons() { - rearrangeButton = requireViewById<Button>(R.id.rearrange).apply { - text = if (fromEditing) { - getString(R.string.controls_favorite_back_to_editing) - } else { - getString(R.string.controls_favorite_rearrange_button) - } - isEnabled = false - visibility = View.VISIBLE - setOnClickListener { - if (component == null) return@setOnClickListener - saveFavorites() - startActivity( - Intent(context, ControlsEditingActivity::class.java).also { - it.putExtra(Intent.EXTRA_COMPONENT_NAME, component) - it.putExtra(ControlsEditingActivity.EXTRA_APP, appName) - it.putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, true) - it.putExtra( - ControlsEditingActivity.EXTRA_STRUCTURE, - listOfStructures[structurePager.currentItem].structureName, - ) - }, - ActivityOptions - .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle() - ) + rearrangeButton = + requireViewById<Button>(R.id.rearrange).apply { + text = + if (fromEditing) { + getString(R.string.controls_favorite_back_to_editing) + } else { + getString(R.string.controls_favorite_rearrange_button) + } + isEnabled = false + visibility = View.VISIBLE + setOnClickListener { + if (component == null) return@setOnClickListener + saveFavorites() + startActivity( + Intent(context, ControlsEditingActivity::class.java).also { + it.putExtra(Intent.EXTRA_COMPONENT_NAME, component) + it.putExtra(ControlsEditingActivity.EXTRA_APP, appName) + it.putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, true) + it.putExtra( + ControlsEditingActivity.EXTRA_STRUCTURE, + listOfStructures[structurePager.currentItem].structureName, + ) + }, + ActivityOptions.makeSceneTransitionAnimation( + this@ControlsFavoritingActivity + ) + .toBundle(), + ) + } } - } - doneButton = requireViewById<Button>(R.id.done).apply { - isEnabled = false - setOnClickListener { - if (component == null) return@setOnClickListener - saveFavorites() - animateExitAndFinish() - openControlsOrigin() + doneButton = + requireViewById<Button>(R.id.done).apply { + isEnabled = false + setOnClickListener { + if (component == null) return@setOnClickListener + saveFavorites() + animateExitAndFinish() + openControlsOrigin() + } } - } } private fun saveFavorites() { @@ -362,14 +415,14 @@ open class ControlsFavoritingActivity @Inject constructor( private fun openControlsOrigin() { startActivity( Intent(applicationContext, ControlsActivity::class.java), - ActivityOptions.makeSceneTransitionAnimation(this).toBundle() + ActivityOptions.makeSceneTransitionAnimation(this).toBundle(), ) } override fun onPause() { super.onPause() mTooltipManager?.hide(false) - } + } override fun onStart() { super.onStart() @@ -380,7 +433,9 @@ open class ControlsFavoritingActivity @Inject constructor( Log.d(TAG, "Registered onBackInvokedCallback") } onBackInvokedDispatcher.registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback) + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + mOnBackInvokedCallback, + ) } override fun onResume() { @@ -393,7 +448,7 @@ open class ControlsFavoritingActivity @Inject constructor( loadControls() isPagerLoaded = true } - } + } override fun onStop() { super.onStop() @@ -403,8 +458,7 @@ open class ControlsFavoritingActivity @Inject constructor( if (DEBUG) { Log.d(TAG, "Unregistered onBackInvokedCallback") } - onBackInvokedDispatcher.unregisterOnBackInvokedCallback( - mOnBackInvokedCallback) + onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback) } override fun onConfigurationChanged(newConfig: Configuration) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsManagementActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsManagementActivity.kt new file mode 100644 index 000000000000..c3b8cff7e4eb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsManagementActivity.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.management + +import android.app.Activity +import android.view.View +import android.view.ViewGroup +import android.view.WindowInsets +import android.view.WindowInsets.Type + +interface ControlsManagementActivity { + val activity: Activity +} + +fun ControlsManagementActivity.applyInsets(viewId: Int) { + activity.requireViewById<ViewGroup>(viewId).apply { + setOnApplyWindowInsetsListener { v: View, insets: WindowInsets -> + v.apply { + val paddings = insets.getInsets(Type.systemBars() or Type.displayCutout()) + setPadding(paddings.left, paddings.top, paddings.right, paddings.bottom) + } + + WindowInsets.CONSUMED + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt index 10a0117f8757..f56cd00baa15 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.management +import android.app.Activity import android.app.ActivityOptions import android.app.Dialog import android.content.ComponentName @@ -35,7 +36,6 @@ import androidx.activity.ComponentActivity import androidx.annotation.VisibleForTesting import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.android.systemui.res.R import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.panels.AuthorizedPanelsRepository @@ -43,40 +43,46 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.controls.ui.SelectedItem import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import java.util.concurrent.Executor import javax.inject.Inject -/** - * Activity to select an application to favorite the [Control] provided by them. - */ -open class ControlsProviderSelectorActivity @Inject constructor( +/** Activity to select an application to favorite the [Control] provided by them. */ +open class ControlsProviderSelectorActivity +@Inject +constructor( @Main private val executor: Executor, @Background private val backExecutor: Executor, private val listingController: ControlsListingController, private val controlsController: ControlsController, private val userTracker: UserTracker, private val authorizedPanelsRepository: AuthorizedPanelsRepository, - private val panelConfirmationDialogFactory: PanelConfirmationDialogFactory -) : ComponentActivity() { + private val panelConfirmationDialogFactory: PanelConfirmationDialogFactory, +) : ComponentActivity(), ControlsManagementActivity { companion object { private const val DEBUG = false private const val TAG = "ControlsProviderSelectorActivity" const val BACK_SHOULD_EXIT = "back_should_exit" } + + override val activity: Activity + get() = this + private var backShouldExit = false private lateinit var recyclerView: RecyclerView - private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback { - private val startingUser = listingController.currentUserId + private val userTrackerCallback: UserTracker.Callback = + object : UserTracker.Callback { + private val startingUser = listingController.currentUserId - override fun onUserChanged(newUser: Int, userContext: Context) { - if (newUser != startingUser) { - userTracker.removeCallback(this) - finish() + override fun onUserChanged(newUser: Int, userContext: Context) { + if (newUser != startingUser) { + userTracker.removeCallback(this) + finish() + } } } - } private var dialog: Dialog? = null private val mOnBackInvokedCallback = OnBackInvokedCallback { @@ -95,10 +101,12 @@ open class ControlsProviderSelectorActivity @Inject constructor( ControlsAnimations.observerForAnimations( requireViewById<ViewGroup>(R.id.controls_management_root), window, - intent + intent, ) ) + applyInsets(R.id.controls_management_root) + requireViewById<ViewStub>(R.id.stub).apply { layoutResource = R.layout.controls_management_apps inflate() @@ -114,9 +122,7 @@ open class ControlsProviderSelectorActivity @Inject constructor( requireViewById<Button>(R.id.other_apps).apply { visibility = View.VISIBLE setText(com.android.internal.R.string.cancel) - setOnClickListener { - onBackPressed() - } + setOnClickListener { onBackPressed() } } requireViewById<View>(R.id.done).visibility = View.GONE @@ -125,9 +131,10 @@ open class ControlsProviderSelectorActivity @Inject constructor( override fun onBackPressed() { if (!backShouldExit) { - val i = Intent().apply { - component = ComponentName(applicationContext, ControlsActivity::class.java) - } + val i = + Intent().apply { + component = ComponentName(applicationContext, ControlsActivity::class.java) + } startActivity(i, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) } animateExitAndFinish() @@ -138,33 +145,40 @@ open class ControlsProviderSelectorActivity @Inject constructor( userTracker.addCallback(userTrackerCallback, executor) recyclerView.alpha = 0.0f - recyclerView.adapter = AppAdapter( - backExecutor, - executor, - lifecycle, - listingController, - LayoutInflater.from(this), - ::onAppSelected, - FavoritesRenderer(resources, controlsController::countFavoritesForComponent), - resources, - authorizedPanelsRepository.getAuthorizedPanels() - ).apply { - registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - var hasAnimated = false - override fun onChanged() { - if (!hasAnimated) { - hasAnimated = true - ControlsAnimations.enterAnimation(recyclerView).start() - } + recyclerView.adapter = + AppAdapter( + backExecutor, + executor, + lifecycle, + listingController, + LayoutInflater.from(this), + ::onAppSelected, + FavoritesRenderer(resources, controlsController::countFavoritesForComponent), + resources, + authorizedPanelsRepository.getAuthorizedPanels(), + ) + .apply { + registerAdapterDataObserver( + object : RecyclerView.AdapterDataObserver() { + var hasAnimated = false + + override fun onChanged() { + if (!hasAnimated) { + hasAnimated = true + ControlsAnimations.enterAnimation(recyclerView).start() + } + } + } + ) } - }) - } if (DEBUG) { Log.d(TAG, "Registered onBackInvokedCallback") } onBackInvokedDispatcher.registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback) + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + mOnBackInvokedCallback, + ) } override fun onStop() { @@ -184,38 +198,45 @@ open class ControlsProviderSelectorActivity @Inject constructor( launchFavoritingActivity(serviceInfo.componentName) } else { val appName = serviceInfo.loadLabel() ?: "" - dialog = panelConfirmationDialogFactory.createConfirmationDialog(this, appName) { ok -> - if (ok) { - authorizedPanelsRepository.addAuthorizedPanels( - setOf(serviceInfo.componentName.packageName) - ) - val selected = SelectedItem.PanelItem(appName, serviceInfo.componentName) - controlsController.setPreferredSelection(selected) - animateExitAndFinish() - openControlsOrigin() - } - dialog = null - }.also { it.show() } + dialog = + panelConfirmationDialogFactory + .createConfirmationDialog(this, appName) { ok -> + if (ok) { + authorizedPanelsRepository.addAuthorizedPanels( + setOf(serviceInfo.componentName.packageName) + ) + val selected = + SelectedItem.PanelItem(appName, serviceInfo.componentName) + controlsController.setPreferredSelection(selected) + animateExitAndFinish() + openControlsOrigin() + } + dialog = null + } + .also { it.show() } } } /** * Launch the [ControlsFavoritingActivity] for the specified component. + * * @param component a component name for a [ControlsProviderService] */ private fun launchFavoritingActivity(component: ComponentName?) { executor.execute { component?.let { - val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java) - .apply { - putExtra(ControlsFavoritingActivity.EXTRA_APP, - listingController.getAppLabel(it)) - putExtra(Intent.EXTRA_COMPONENT_NAME, it) - putExtra( - ControlsFavoritingActivity.EXTRA_SOURCE, - ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR, - ) - } + val intent = + Intent(applicationContext, ControlsFavoritingActivity::class.java).apply { + putExtra( + ControlsFavoritingActivity.EXTRA_APP, + listingController.getAppLabel(it), + ) + putExtra(Intent.EXTRA_COMPONENT_NAME, it) + putExtra( + ControlsFavoritingActivity.EXTRA_SOURCE, + ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR, + ) + } startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) } } @@ -228,8 +249,8 @@ open class ControlsProviderSelectorActivity @Inject constructor( private fun openControlsOrigin() { startActivity( - Intent(applicationContext, ControlsActivity::class.java), - ActivityOptions.makeSceneTransitionAnimation(this).toBundle() + Intent(applicationContext, ControlsActivity::class.java), + ActivityOptions.makeSceneTransitionAnimation(this).toBundle(), ) } @@ -242,7 +263,8 @@ open class ControlsProviderSelectorActivity @Inject constructor( override fun run() { finish() } - } - ).start() + }, + ) + .start() } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt index 955a5a35d82f..8296ec2311b6 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.ui +import android.app.Activity import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -25,36 +26,40 @@ import android.content.res.Configuration import android.os.Bundle import android.os.RemoteException import android.service.dreams.IDreamManager -import android.view.View import android.view.ViewGroup -import android.view.WindowInsets -import android.view.WindowInsets.Type import android.view.WindowManager import androidx.activity.ComponentActivity -import com.android.systemui.res.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.management.ControlsAnimations +import com.android.systemui.controls.management.ControlsManagementActivity +import com.android.systemui.controls.management.applyInsets import com.android.systemui.controls.settings.ControlsSettingsDialogManager import com.android.systemui.flags.FeatureFlags +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject /** * Displays Device Controls inside an activity. This activity is available to be displayed over the * lockscreen if the user has allowed it via - * [android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS]. This activity will be - * destroyed on SCREEN_OFF events, due to issues with occluded activities over lockscreen as well as - * user expectations for the activity to not continue running. + * [android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS]. This activity will be destroyed on + * SCREEN_OFF events, due to issues with occluded activities over lockscreen as well as user + * expectations for the activity to not continue running. */ // Open for testing -open class ControlsActivity @Inject constructor( +open class ControlsActivity +@Inject +constructor( private val uiController: ControlsUiController, private val broadcastDispatcher: BroadcastDispatcher, private val dreamManager: IDreamManager, private val featureFlags: FeatureFlags, private val controlsSettingsDialogManager: ControlsSettingsDialogManager, - private val keyguardStateController: KeyguardStateController -) : ComponentActivity() { + private val keyguardStateController: KeyguardStateController, +) : ComponentActivity(), ControlsManagementActivity { + + override val activity: Activity + get() = this private val lastConfiguration = Configuration() @@ -69,38 +74,27 @@ open class ControlsActivity @Inject constructor( setContentView(R.layout.controls_fullscreen) + applyInsets(R.id.control_detail_root) + lifecycle.addObserver( ControlsAnimations.observerForAnimations( requireViewById(R.id.control_detail_root), window, intent, - false + false, ) ) - requireViewById<ViewGroup>(R.id.control_detail_root).apply { - setOnApplyWindowInsetsListener { - v: View, insets: WindowInsets -> - v.apply { - val l = getPaddingLeft() - val t = getPaddingTop() - val r = getPaddingRight() - setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom) - } - - WindowInsets.CONSUMED - } - } - initBroadcastReceiver() } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - val interestingFlags = ActivityInfo.CONFIG_ORIENTATION or + val interestingFlags = + ActivityInfo.CONFIG_ORIENTATION or ActivityInfo.CONFIG_SCREEN_SIZE or ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE - if (lastConfiguration.diff(newConfig) and interestingFlags != 0 ) { + if (lastConfiguration.diff(newConfig) and interestingFlags != 0) { uiController.onSizeChange() } lastConfiguration.setTo(newConfig) @@ -164,15 +158,18 @@ open class ControlsActivity @Inject constructor( } private fun initBroadcastReceiver() { - broadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.getAction() - if (action == Intent.ACTION_SCREEN_OFF || - action == Intent.ACTION_DREAMING_STARTED) { - finish() + broadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.getAction() + if ( + action == Intent.ACTION_SCREEN_OFF || + action == Intent.ACTION_DREAMING_STARTED + ) { + finish() + } } } - } val filter = IntentFilter() filter.addAction(Intent.ACTION_SCREEN_OFF) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 2e323d40edcd..1fc549469b55 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -75,7 +75,7 @@ import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule; import com.android.systemui.statusbar.notification.dagger.ReferenceNotificationsModule; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeServiceHost; -import com.android.systemui.statusbar.phone.HeadsUpModule; +import com.android.systemui.statusbar.notification.headsup.HeadsUpModule; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 38cfb9b333a8..fcc3ea9f7d58 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -54,7 +54,7 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.ImmersiveModeConfirmation import com.android.systemui.statusbar.gesture.GesturePointerEventListener import com.android.systemui.statusbar.notification.InstantAppNotifier -import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener +import com.android.systemui.statusbar.notification.headsup.StatusBarHeadsUpChangeListener import com.android.systemui.statusbar.policy.BatteryControllerStartable import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index b7d3c9253f51..d6f8957ace33 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -32,6 +32,7 @@ import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.CameraProtectionModule; import com.android.systemui.CoreStartable; import com.android.systemui.SystemUISecondaryUserService; +import com.android.systemui.activity.ActivityManagerModule; import com.android.systemui.ambient.dagger.AmbientModule; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; @@ -140,7 +141,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.ConfigurationControllerModule; import com.android.systemui.statusbar.phone.LetterboxModule; import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PolicyModule; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; @@ -198,6 +199,7 @@ import javax.inject.Named; * may not appreciate that. */ @Module(includes = { + ActivityManagerModule.class, AmbientModule.class, AppOpsModule.class, AssistModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt index e862525623fe..9b181be93b61 100644 --- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt +++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt @@ -30,6 +30,7 @@ import com.android.systemui.display.data.repository.FocusedDisplayRepository import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl +import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor import com.android.systemui.display.domain.interactor.RearDisplayStateInteractorImpl import com.android.systemui.statusbar.core.StatusBarConnectedDisplays @@ -41,7 +42,7 @@ import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap /** Module binding display related classes. */ -@Module +@Module(includes = [DisplayWindowPropertiesInteractorModule::class]) interface DisplayModule { @Binds fun bindConnectedDisplayInteractor( diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt new file mode 100644 index 000000000000..22e467bd5e3c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.domain.interactor + +import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository +import com.android.systemui.display.shared.model.DisplayWindowProperties +import dagger.Binds +import dagger.Module +import javax.inject.Inject + +/** Provides per display instances of [DisplayWindowProperties]. */ +interface DisplayWindowPropertiesInteractor { + + /** + * Returns a [DisplayWindowProperties] instance for a given display id, to be used for the + * status bar. + * + * @throws IllegalArgumentException if no display with the given display id exists. + */ + fun getForStatusBar(displayId: Int): DisplayWindowProperties +} + +@SysUISingleton +class DisplayWindowPropertiesInteractorImpl +@Inject +constructor(private val repo: DisplayWindowPropertiesRepository) : + DisplayWindowPropertiesInteractor { + + override fun getForStatusBar(displayId: Int): DisplayWindowProperties { + return repo.get(displayId, TYPE_STATUS_BAR) + } +} + +@Module +interface DisplayWindowPropertiesInteractorModule { + + @Binds + fun interactor(impl: DisplayWindowPropertiesInteractorImpl): DisplayWindowPropertiesInteractor +} diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt index 2a3729b1aeab..c49ba80c660b 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt @@ -27,6 +27,7 @@ import android.os.Bundle import android.os.UserHandle import android.view.accessibility.AccessibilityManager import androidx.core.app.NotificationCompat +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -39,7 +40,6 @@ import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutoria import com.android.systemui.res.R import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import com.android.app.tracing.coroutines.launchTraced as launch /** * A class to show contextual education on UI based on the edu produced from @@ -107,6 +107,7 @@ constructor( } private fun showDialog(model: ContextualEduToastViewModel) { + dialog?.dismiss() dialog = createDialog(model) dialog?.show() } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt index 2be619bac998..abd39cc8dea8 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt @@ -23,13 +23,16 @@ import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.node.Ref @@ -93,13 +96,19 @@ private fun EducationAnimation( animationProperties: LottieDynamicProperties, ) { val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId)) + var isPlaying by remember { mutableStateOf(true) } val progress by - animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever) + animateLottieCompositionAsState( + composition, + iterations = LottieConstants.IterateForever, + isPlaying = isPlaying, + restartOnPlay = false, + ) LottieAnimation( composition = composition, progress = { progress }, dynamicProperties = animationProperties, - modifier = Modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize().clickable { isPlaying = !isPlaying }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt index a0897f293624..27d1a30f4346 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt @@ -116,7 +116,10 @@ constructor( .filter { // Allow KEYCODE_UNKNOWN (0) because shortcuts can have just modifiers and no // keycode, or they could have a baseCharacter instead of a keycode. - it.keycode == KeyEvent.KEYCODE_UNKNOWN || supportedKeyCodes.contains(it.keycode) + it.keycode == KeyEvent.KEYCODE_UNKNOWN || + supportedKeyCodes.contains(it.keycode) || + // Support keyboard function row key codes + keyGlyphMap?.functionRowKeys?.contains(it.keycode) ?: false } .mapNotNull { toShortcut(keyGlyphMap, keyCharacterMap, it, keepIcons) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt index e47b33f8c37c..d91922ddb65e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt @@ -99,6 +99,7 @@ import android.view.KeyEvent.KEYCODE_PAGE_DOWN import android.view.KeyEvent.KEYCODE_PAGE_UP import android.view.KeyEvent.KEYCODE_PERIOD import android.view.KeyEvent.KEYCODE_RECENT_APPS +import android.view.KeyEvent.KEYCODE_SCREENSHOT import android.view.KeyEvent.KEYCODE_SCROLL_LOCK import android.view.KeyEvent.KEYCODE_SHIFT_LEFT import android.view.KeyEvent.KEYCODE_SHIFT_RIGHT @@ -125,6 +126,14 @@ object ShortcutHelperKeys { KEYCODE_RECENT_APPS to R.drawable.ic_check_box_outline_blank, ) + val keyLabelResIds = + mapOf( + KEYCODE_BACK to R.string.group_system_go_back, + KEYCODE_HOME to R.string.group_system_access_home_screen, + KEYCODE_RECENT_APPS to R.string.group_system_overview_open_apps, + KEYCODE_SCREENSHOT to R.string.group_system_full_screenshot, + ) + val modifierLabels = mapOf<Int, (Context) -> String>( // Modifiers @@ -140,6 +149,7 @@ object ShortcutHelperKeys { mapOf<Int, (Context) -> String>( KEYCODE_HOME to { context -> context.getString(R.string.keyboard_key_home) }, KEYCODE_BACK to { context -> context.getString(R.string.keyboard_key_back) }, + KEYCODE_RECENT_APPS to { context -> context.getString(R.string.accessibility_recent) }, KEYCODE_DPAD_UP to { context -> context.getString(R.string.keyboard_key_dpad_up) }, KEYCODE_DPAD_DOWN to { context -> context.getString(R.string.keyboard_key_dpad_down) }, KEYCODE_DPAD_LEFT to { context -> context.getString(R.string.keyboard_key_dpad_left) }, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt index 1b2098650278..4787507dac39 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt @@ -17,12 +17,16 @@ package com.android.systemui.keyboard.shortcut.data.source import android.content.res.Resources +import android.hardware.input.InputManager +import android.view.KeyEvent.KEYCODE_EMOJI_PICKER import android.view.KeyEvent.KEYCODE_SPACE import android.view.KeyEvent.META_CTRL_ON import android.view.KeyEvent.META_SHIFT_ON import android.view.KeyboardShortcutGroup +import android.view.KeyboardShortcutInfo import android.view.WindowManager import android.view.WindowManager.KeyboardShortcutsReceiver +import com.android.systemui.Flags.shortcutHelperKeyGlyph import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo import com.android.systemui.res.R @@ -31,16 +35,19 @@ import kotlinx.coroutines.suspendCancellableCoroutine class InputShortcutsSource @Inject -constructor(@Main private val resources: Resources, private val windowManager: WindowManager) : - KeyboardShortcutGroupsSource { +constructor( + @Main private val resources: Resources, + private val windowManager: WindowManager, + private val inputManager: InputManager, +) : KeyboardShortcutGroupsSource { override suspend fun shortcutGroups(deviceId: Int): List<KeyboardShortcutGroup> = - getInputLanguageShortcutGroup() + getImeShortcutGroup(deviceId) + getInputLanguageShortcutGroup(deviceId) + getImeShortcutGroup(deviceId) - private fun getInputLanguageShortcutGroup() = + private fun getInputLanguageShortcutGroup(deviceId: Int) = listOf( KeyboardShortcutGroup( resources.getString(R.string.shortcut_helper_category_input), - inputLanguageShortcuts() + inputLanguageShortcuts() + hardwareShortcuts(deviceId), ) ) @@ -53,9 +60,23 @@ constructor(@Main private val resources: Resources, private val windowManager: W /* Switch previous language (next language): Ctrl + Shift + Space */ shortcutInfo(resources.getString(R.string.input_switch_input_language_previous)) { command(META_CTRL_ON or META_SHIFT_ON, KEYCODE_SPACE) - } + }, ) + private fun hardwareShortcuts(deviceId: Int): List<KeyboardShortcutInfo> { + if (shortcutHelperKeyGlyph()) { + val keyGlyphMap = inputManager.getKeyGlyphMap(deviceId) + if (keyGlyphMap != null && keyGlyphMap.functionRowKeys.contains(KEYCODE_EMOJI_PICKER)) { + return listOf( + shortcutInfo(resources.getString(R.string.input_access_emoji)) { + command(modifiers = 0, KEYCODE_EMOJI_PICKER) + } + ) + } + } + return emptyList() + } + private suspend fun getImeShortcutGroup(deviceId: Int): List<KeyboardShortcutGroup> = suspendCancellableCoroutine { continuation -> val shortcutsReceiver = KeyboardShortcutsReceiver { diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt index 0201f402af2a..5ef869e6d848 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt @@ -16,24 +16,34 @@ package com.android.systemui.keyboard.shortcut.data.source +import android.content.Context import android.content.res.Resources import android.view.KeyEvent.KEYCODE_D import android.view.KeyEvent.KEYCODE_DPAD_LEFT import android.view.KeyEvent.KEYCODE_DPAD_RIGHT import android.view.KeyEvent.KEYCODE_DPAD_UP +import android.view.KeyEvent.KEYCODE_EQUALS +import android.view.KeyEvent.KEYCODE_LEFT_BRACKET +import android.view.KeyEvent.KEYCODE_MINUS +import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET import android.view.KeyEvent.KEYCODE_TAB import android.view.KeyEvent.META_ALT_ON import android.view.KeyEvent.META_CTRL_ON import android.view.KeyEvent.META_META_ON import android.view.KeyEvent.META_SHIFT_ON import android.view.KeyboardShortcutGroup +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo import com.android.systemui.res.R import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut +import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import javax.inject.Inject -class MultitaskingShortcutsSource @Inject constructor(@Main private val resources: Resources) : +class MultitaskingShortcutsSource +@Inject +constructor(@Main private val resources: Resources, @Application private val context: Context) : KeyboardShortcutGroupsSource { override suspend fun shortcutGroups(deviceId: Int) = @@ -95,6 +105,40 @@ class MultitaskingShortcutsSource @Inject constructor(@Main private val resource } ) } + if ( + DesktopModeStatus.canEnterDesktopMode(context) && enableTaskResizingKeyboardShortcuts() + ) { + // Snap a freeform window to the left + // - Meta + Left bracket + add( + shortcutInfo(resources.getString(R.string.system_desktop_mode_snap_left_window)) { + command(META_META_ON, KEYCODE_LEFT_BRACKET) + } + ) + // Snap a freeform window to the right + // - Meta + Right bracket + add( + shortcutInfo(resources.getString(R.string.system_desktop_mode_snap_right_window)) { + command(META_META_ON, KEYCODE_RIGHT_BRACKET) + } + ) + // Toggle maximize a freeform window + // - Meta + Equals + add( + shortcutInfo( + resources.getString(R.string.system_desktop_mode_toggle_maximize_window) + ) { + command(META_META_ON, KEYCODE_EQUALS) + } + ) + // Minimize a freeform window + // - Meta + Minus + add( + shortcutInfo(resources.getString(R.string.system_desktop_mode_minimize_window)) { + command(META_META_ON, KEYCODE_MINUS) + } + ) + } } private fun recentsShortcuts() = diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt index 7c0c75ef2c4c..a650cd889381 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt @@ -17,6 +17,8 @@ package com.android.systemui.keyboard.shortcut.data.source import android.content.res.Resources +import android.hardware.input.InputManager +import android.hardware.input.KeyGlyphMap import android.view.KeyEvent.KEYCODE_A import android.view.KeyEvent.KEYCODE_BACK import android.view.KeyEvent.KEYCODE_DEL @@ -35,26 +37,87 @@ import android.view.KeyEvent.KEYCODE_TAB import android.view.KeyEvent.META_CTRL_ON import android.view.KeyEvent.META_META_ON import android.view.KeyboardShortcutGroup +import android.view.KeyboardShortcutInfo +import com.android.systemui.Flags.shortcutHelperKeyGlyph import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo +import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys import com.android.systemui.res.R import javax.inject.Inject -class SystemShortcutsSource @Inject constructor(@Main private val resources: Resources) : +class SystemShortcutsSource +@Inject +constructor(@Main private val resources: Resources, private val inputManager: InputManager) : KeyboardShortcutGroupsSource { override suspend fun shortcutGroups(deviceId: Int) = listOf( KeyboardShortcutGroup( resources.getString(R.string.shortcut_helper_category_system_controls), - systemControlsShortcuts() + hardwareShortcuts(deviceId) + systemControlsShortcuts(), ), KeyboardShortcutGroup( resources.getString(R.string.shortcut_helper_category_system_apps), - systemAppsShortcuts() - ) + systemAppsShortcuts(), + ), ) + private fun hardwareShortcuts(deviceId: Int): List<KeyboardShortcutInfo> = + if (shortcutHelperKeyGlyph()) { + val keyGlyphMap = inputManager.getKeyGlyphMap(deviceId) + if (keyGlyphMap != null) { + functionRowKeys(keyGlyphMap) + keyCombinationShortcuts(keyGlyphMap) + } else { + // Not add function row keys if it is not supported by keyboard + emptyList() + } + } else { + defaultFunctionRowKeys() + } + + private fun defaultFunctionRowKeys(): List<KeyboardShortcutInfo> = + listOf( + shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) { + command(modifiers = 0, KEYCODE_HOME) + }, + shortcutInfo(resources.getString(R.string.group_system_go_back)) { + command(modifiers = 0, KEYCODE_BACK) + }, + shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) { + command(modifiers = 0, KEYCODE_RECENT_APPS) + }, + ) + + private fun functionRowKeys(keyGlyphMap: KeyGlyphMap): List<KeyboardShortcutInfo> { + val functionRowKeys = mutableListOf<KeyboardShortcutInfo>() + keyGlyphMap.functionRowKeys.forEach { keyCode -> + val labelResId = ShortcutHelperKeys.keyLabelResIds[keyCode] + if (labelResId != null) { + functionRowKeys.add( + shortcutInfo(resources.getString(labelResId)) { + command(modifiers = 0, keyCode) + } + ) + } + } + return functionRowKeys + } + + private fun keyCombinationShortcuts(keyGlyphMap: KeyGlyphMap): List<KeyboardShortcutInfo> { + val shortcuts = mutableListOf<KeyboardShortcutInfo>() + keyGlyphMap.hardwareShortcuts.forEach { (keyCombination, keyCode) -> + val labelResId = ShortcutHelperKeys.keyLabelResIds[keyCode] + if (labelResId != null) { + val info = + shortcutInfo(resources.getString(labelResId)) { + command(keyCombination.modifierState, keyCombination.keycode) + } + shortcuts.add(info) + } + } + return shortcuts + } + private fun systemControlsShortcuts() = listOf( // Access list of all apps and search (i.e. Search/Launcher): @@ -63,36 +126,24 @@ class SystemShortcutsSource @Inject constructor(@Main private val resources: Res command(META_META_ON) }, // Access home screen: - // - Home button // - Meta + H // - Meta + Enter shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) { - command(modifiers = 0, KEYCODE_HOME) - }, - shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) { command(META_META_ON, KEYCODE_H) }, shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) { command(META_META_ON, KEYCODE_ENTER) }, // Overview of open apps: - // - Recent apps button // - Meta + Tab shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) { - command(modifiers = 0, KEYCODE_RECENT_APPS) - }, - shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) { command(META_META_ON, KEYCODE_TAB) }, // Back: go back to previous state (back button) - // - Back button // - Meta + Escape OR // - Meta + Backspace OR // - Meta + Left arrow shortcutInfo(resources.getString(R.string.group_system_go_back)) { - command(modifiers = 0, KEYCODE_BACK) - }, - shortcutInfo(resources.getString(R.string.group_system_go_back)) { command(META_META_ON, KEYCODE_ESCAPE) }, shortcutInfo(resources.getString(R.string.group_system_go_back)) { diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt index 0381eaec5026..2385cc6a4c47 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt @@ -16,14 +16,22 @@ package com.android.systemui.keyboard.shortcut.domain.interactor +import android.content.Context +import android.view.KeyEvent.META_META_ON import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys +import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys.metaModifierIconResId import com.android.systemui.keyboard.shortcut.qualifiers.CustomShortcutCategories import com.android.systemui.keyboard.shortcut.qualifiers.DefaultShortcutCategories import com.android.systemui.keyboard.shortcut.shared.model.Shortcut import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory +import com.android.systemui.res.R import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -34,6 +42,7 @@ import kotlinx.coroutines.flow.flowOf class ShortcutHelperCategoriesInteractor @Inject constructor( + @Application private val context: Context, @DefaultShortcutCategories defaultCategoriesRepository: ShortcutCategoriesRepository, @CustomShortcutCategories customCategoriesRepositoryLazy: Lazy<ShortcutCategoriesRepository>, ) { @@ -87,6 +96,54 @@ constructor( label = commonLabel, icon = groupedShortcuts.firstOrNull()?.icon, commands = groupedShortcuts.flatMap { it.commands }, + contentDescription = + toContentDescription(commonLabel, groupedShortcuts.flatMap { it.commands }), ) } + + private fun toContentDescription(label: String, commands: List<ShortcutCommand>): String { + val pressKey = context.getString(R.string.shortcut_helper_add_shortcut_dialog_placeholder) + val andConjunction = + context.getString(R.string.shortcut_helper_key_combinations_and_conjunction) + val orConjunction = + context.getString(R.string.shortcut_helper_key_combinations_or_separator) + val forwardSlash = + context.getString(R.string.shortcut_helper_key_combinations_forward_slash) + return buildString { + append("$label, $pressKey") + commands.forEachIndexed { i, shortcutCommand -> + if (i > 0) { + append(", $orConjunction") + } + shortcutCommand.keys.forEachIndexed { j, shortcutKey -> + if (j > 0) { + append(" $andConjunction") + } + if (shortcutKey is ShortcutKey.Text) { + // Special handling for "/" as TalkBack will not read punctuation by + // default. + if (shortcutKey.value.equals("/")) { + append(" $forwardSlash") + } else { + append(" ${shortcutKey.value}") + } + } else if (shortcutKey is ShortcutKey.Icon.ResIdIcon) { + val keyLabel = + if (shortcutKey.drawableResId == metaModifierIconResId) { + ShortcutHelperKeys.modifierLabels[META_META_ON] + } else { + val keyCode = + ShortcutHelperKeys.keyIcons.entries + .firstOrNull { it.value == shortcutKey.drawableResId } + ?.key + ShortcutHelperKeys.specialKeyLabels[keyCode] + } + if (keyLabel != null) { + append(" ${keyLabel.invoke(context)}") + } + } // No-Op when shortcutKey is ShortcutKey.Icon.DrawableIcon + } + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt index bf7df7ef8561..9cc15ce809e9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt @@ -20,18 +20,24 @@ data class Shortcut( val label: String, val commands: List<ShortcutCommand>, val icon: ShortcutIcon? = null, + val contentDescription: String = "", ) { val containsCustomShortcutCommands: Boolean = commands.any { it.isCustom } } class ShortcutBuilder(private val label: String) { val commands = mutableListOf<ShortcutCommand>() + var contentDescription = "" fun command(builder: ShortcutCommandBuilder.() -> Unit) { commands += ShortcutCommandBuilder().apply(builder).build() } - fun build() = Shortcut(label, commands) + fun contentDescription(string: () -> String) { + contentDescription = string.invoke() + } + + fun build() = Shortcut(label, commands, contentDescription = contentDescription) } fun shortcut(label: String, block: ShortcutBuilder.() -> Unit): Shortcut = diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt index e3675de5d197..1f37c7de1439 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt @@ -96,6 +96,8 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.hideFromAccessibility import androidx.compose.ui.semantics.isTraversalGroup import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics @@ -111,6 +113,7 @@ import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastForEachIndexed import com.android.compose.modifiers.thenIf import com.android.compose.ui.graphics.painter.rememberDrawablePainter +import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo @@ -121,7 +124,6 @@ import com.android.systemui.keyboard.shortcut.ui.model.IconSource import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState import com.android.systemui.res.R -import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel import kotlinx.coroutines.delay @Composable @@ -465,14 +467,10 @@ private fun EndSidePanel( onCustomizationRequested = { requestInfo -> when (requestInfo) { is ShortcutCustomizationRequestInfo.Add -> - onCustomizationRequested( - requestInfo.copy(categoryType = category.type) - ) + onCustomizationRequested(requestInfo.copy(categoryType = category.type)) is ShortcutCustomizationRequestInfo.Delete -> - onCustomizationRequested( - requestInfo.copy(categoryType = category.type) - ) + onCustomizationRequested(requestInfo.copy(categoryType = category.type)) } }, ) @@ -572,20 +570,31 @@ private fun Shortcut( } .focusable(interactionSource = interactionSource) .padding(8.dp) + .semantics { contentDescription = shortcut.contentDescription } ) { Row( - modifier = Modifier.width(128.dp).align(Alignment.CenterVertically).weight(0.333f), + modifier = + Modifier.width(128.dp).align(Alignment.CenterVertically).weight(0.333f).semantics { + hideFromAccessibility() + }, horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically, ) { if (shortcut.icon != null) { - ShortcutIcon(shortcut.icon, modifier = Modifier.size(24.dp)) + ShortcutIcon( + shortcut.icon, + modifier = Modifier.size(24.dp).semantics { hideFromAccessibility() }, + ) } - ShortcutDescriptionText(searchQuery = searchQuery, shortcut = shortcut) + ShortcutDescriptionText( + searchQuery = searchQuery, + shortcut = shortcut, + modifier = Modifier.semantics { hideFromAccessibility() }, + ) } - Spacer(modifier = Modifier.width(24.dp)) + Spacer(modifier = Modifier.width(24.dp).semantics { hideFromAccessibility() }) ShortcutKeyCombinations( - modifier = Modifier.weight(.666f), + modifier = Modifier.weight(.666f).semantics { hideFromAccessibility() }, shortcut = shortcut, isCustomizing = isCustomizing, onAddShortcutRequested = { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 7097c1d2fc4e..d40fe468b0a5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -81,7 +81,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardLockWhileAwakeInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardServiceLockNowInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor; import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier; @@ -330,8 +330,7 @@ public class KeyguardService extends Service { return new FoldGracePeriodProvider(); } }; - private final KeyguardLockWhileAwakeInteractor - mKeyguardLockWhileAwakeInteractor; + private final KeyguardServiceLockNowInteractor mKeyguardServiceLockNowInteractor; @Inject public KeyguardService( @@ -357,7 +356,7 @@ public class KeyguardService extends Service { KeyguardDismissInteractor keyguardDismissInteractor, Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy, KeyguardStateCallbackInteractor keyguardStateCallbackInteractor, - KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) { + KeyguardServiceLockNowInteractor keyguardServiceLockNowInteractor) { super(); mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; @@ -389,7 +388,7 @@ public class KeyguardService extends Service { mKeyguardEnabledInteractor = keyguardEnabledInteractor; mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor; mKeyguardDismissInteractor = keyguardDismissInteractor; - mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor; + mKeyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor; } @Override @@ -665,7 +664,7 @@ public class KeyguardService extends Service { if (SceneContainerFlag.isEnabled()) { mDeviceEntryInteractorLazy.get().lockNow(); } else if (KeyguardWmStateRefactor.isEnabled()) { - mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options); + mKeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(options); } mKeyguardViewMediator.doKeyguardTimeout(options); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 01ec4d026646..9f131607cb99 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -3943,7 +3943,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void notifyDefaultDisplayCallbacks(boolean showing) { - if (SceneContainerFlag.isEnabled()) { + if (SceneContainerFlag.isEnabled() || KeyguardWmStateRefactor.isEnabled()) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt index 21090c12e9fe..cc8652c76181 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.domain.interactor import com.android.keyguard.logging.KeyguardLogger -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.dagger.SysUISingleton @@ -28,15 +27,12 @@ import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.DismissAction import com.android.systemui.keyguard.shared.model.KeyguardDone import com.android.systemui.keyguard.shared.model.KeyguardState.GONE -import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.log.core.LogLevel -import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -66,8 +62,6 @@ constructor( val dismissInteractor: KeyguardDismissInteractor, @Application private val applicationScope: CoroutineScope, deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>, - powerInteractor: PowerInteractor, - alternateBouncerInteractor: AlternateBouncerInteractor, shadeInteractor: Lazy<ShadeInteractor>, keyguardInteractor: Lazy<KeyguardInteractor>, sceneInteractor: Lazy<SceneInteractor>, @@ -144,42 +138,6 @@ constructor( } } - /** Flow that emits whenever we need to reset the dismiss action */ - private val resetDismissAction: Flow<Unit> = - combine( - if (SceneContainerFlag.isEnabled) { - // Using currentScene instead of isFinishedIn because of a race condition that - // forms between isFinishedIn(Gone) and isOnShadeWhileUnlocked where the latter - // emits false before the former emits true, causing the evaluation of the - // combine to come up with true, temporarily, before settling on false, which is - // a valid final state. That causes an incorrect reset of the dismiss action to - // occur before it gets executed. - sceneInteractor - .get() - .currentScene - .map { it == Scenes.Gone } - .distinctUntilChanged() - } else { - transitionInteractor.isFinishedIn( - scene = Scenes.Gone, - stateWithoutSceneContainer = GONE, - ) - }, - transitionInteractor.isFinishedIn( - scene = Scenes.Bouncer, - stateWithoutSceneContainer = PRIMARY_BOUNCER, - ), - alternateBouncerInteractor.isVisible, - isOnShadeWhileUnlocked, - powerInteractor.isAsleep, - ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked, isAsleep -> - (!isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked) || - isAsleep - } - .filter { it } - .sampleFilter(dismissAction) { it !is DismissAction.None } - .map {} - fun runDismissAnimationOnKeyguard(): Boolean { return willAnimateDismissActionOnLockscreen.value } @@ -220,19 +178,15 @@ constructor( } } - launch { - resetDismissAction.collect { - log("resetDismissAction") - repository.dismissAction.value.onCancelAction.run() - clearDismissAction() - } - } - launch { repository.dismissAction.collect { log("updatedDismissAction=$it") } } awaitCancellation() } } + fun clearDismissAction() { + repository.setDismissAction(DismissAction.None) + } + /** Run the dismiss action and starts the dismiss keyguard transition. */ private suspend fun runDismissAction() { val dismissAction = repository.dismissAction.value @@ -249,10 +203,6 @@ constructor( } } - private fun clearDismissAction() { - repository.setDismissAction(DismissAction.None) - } - private fun log(message: String) { keyguardLogger.log(TAG, LogLevel.DEBUG, message) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt index 631e44aca26d..42cbd7d39248 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt @@ -16,39 +16,52 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.internal.widget.LockPatternUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.kotlin.sample import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch +import kotlinx.coroutines.withContext /** - * Logic around the keyguard being enabled/disabled, per [KeyguardService]. If the keyguard is not - * enabled, the lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE. + * Logic around the keyguard being enabled, disabled, or suppressed via adb. If the keyguard is + * disabled or suppressed, the lockscreen cannot be shown and the device will go from AOD/DOZING + * directly to GONE. * * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold * permission to do so (such as Phone). Some CTS tests also disable keyguard in onCreate or onStart * rather than simply dismissing the keyguard or setting up the device to have Security: None, for * reasons unknown. + * + * Keyguard can be suppressed by calling "adb shell locksettings set-disabled true", which is + * frequently done in tests. If keyguard is suppressed, it won't show even if the keyguard is + * enabled. If keyguard is not suppressed, then we defer to whether keyguard is enabled or disabled. */ @SysUISingleton class KeyguardEnabledInteractor @Inject constructor( - @Application scope: CoroutineScope, + @Application val scope: CoroutineScope, + @Background val backgroundDispatcher: CoroutineDispatcher, val repository: KeyguardRepository, val biometricSettingsRepository: BiometricSettingsRepository, - keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor, + private val selectedUserInteractor: SelectedUserInteractor, + private val lockPatternUtils: LockPatternUtils, + keyguardDismissTransitionInteractor: dagger.Lazy<KeyguardDismissTransitionInteractor>, internalTransitionInteractor: InternalKeyguardTransitionInteractor, ) { @@ -62,6 +75,10 @@ constructor( * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were * locked when it was disabled. + * + * Even if the keyguard is enabled, it's possible for it to be suppressed temporarily via adb. + * If you need to respect that adb command, you will need to use + * [isKeyguardEnabledAndNotSuppressed] instead of using this flow. */ val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled @@ -96,9 +113,9 @@ constructor( val currentTransitionInfo = internalTransitionInteractor.currentTransitionInfoInternal() if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) { - keyguardDismissTransitionInteractor.startDismissKeyguardTransition( - "keyguard disabled" - ) + keyguardDismissTransitionInteractor + .get() + .startDismissKeyguardTransition("keyguard disabled") } } } @@ -116,4 +133,37 @@ constructor( fun isShowKeyguardWhenReenabled(): Boolean { return repository.isShowKeyguardWhenReenabled() } + + /** + * Whether the keyguard is enabled, and has not been suppressed via adb. + * + * There is unfortunately no callback for [isKeyguardSuppressed], which means this can't be a + * flow, since it's ambiguous when we would query the latest suppression value. + */ + suspend fun isKeyguardEnabledAndNotSuppressed(): Boolean { + return isKeyguardEnabled.value && !isKeyguardSuppressed() + } + + /** + * Returns whether the lockscreen has been disabled ("suppressed") via "adb shell locksettings + * set-disabled". If suppressed, we'll ignore all signals that would typically result in showing + * the keyguard, regardless of the value of [isKeyguardEnabled]. + * + * It's extremely confusing to have [isKeyguardEnabled] not be the inverse of "is lockscreen + * disabled", so this method intentionally re-terms it as "suppressed". + * + * Note that if the lockscreen is currently showing when it's suppressed, it will remain visible + * until it's unlocked, at which point it will never re-appear until suppression is removed. + */ + suspend fun isKeyguardSuppressed( + userId: Int = selectedUserInteractor.getSelectedUserId() + ): Boolean { + // isLockScreenDisabled returns true whenever keyguard is not enabled, even if the adb + // command was not used to disable/suppress the lockscreen. To make these booleans as clear + // as possible, only return true if keyguard is suppressed when it otherwise would have + // been enabled. + return withContext(backgroundDispatcher) { + isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt index 0ab3e5c0927f..ce84e71a3562 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt @@ -16,27 +16,16 @@ package com.android.systemui.keyguard.domain.interactor -import android.os.Bundle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge -/** - * Emitted when we receive a [KeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout] - * call. - * - * Includes a timestamp so it's not conflated by the StateFlow. - */ -data class KeyguardTimeoutWhileAwakeEvent(val timestamp: Long, val options: Bundle?) - /** The reason we're locking while awake, used for logging. */ enum class LockWhileAwakeReason(private val logReason: String) { LOCKDOWN("Lockdown initiated."), @@ -71,10 +60,8 @@ class KeyguardLockWhileAwakeInteractor constructor( biometricSettingsRepository: BiometricSettingsRepository, keyguardEnabledInteractor: KeyguardEnabledInteractor, + keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor, ) { - /** Emits whenever a timeout event is received by [KeyguardService]. */ - private val timeoutEvents: MutableStateFlow<KeyguardTimeoutWhileAwakeEvent?> = - MutableStateFlow(null) /** Emits whenever the current user is in lockdown mode. */ private val inLockdown: Flow<LockWhileAwakeReason> = @@ -97,25 +84,19 @@ constructor( /** Emits whenever we should lock while the screen is on, for any reason. */ val lockWhileAwakeEvents: Flow<LockWhileAwakeReason> = merge( - inLockdown, - keyguardReenabled, - timeoutEvents.filterNotNull().map { - LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON - }, + // We're in lockdown, and the keyguard is enabled. If the keyguard is disabled, the + // lockdown button is hidden in the UI, but it's still possible to trigger lockdown in + // tests. + inLockdown + .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() } + .map { LockWhileAwakeReason.LOCKDOWN }, + // The keyguard was re-enabled, and it was showing when it was originally disabled. + // Tests currently expect that if the keyguard is re-enabled, it will show even if it's + // suppressed, so we don't check for isKeyguardEnabledAndNotSuppressed() on this flow. + keyguardReenabled.map { LockWhileAwakeReason.KEYGUARD_REENABLED }, + // KeyguardService says we need to lock now, and the lockscreen is enabled. + keyguardServiceLockNowInteractor.lockNowEvents + .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() } + .map { LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON }, ) - - /** - * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that - * the device locked while the screen was on. - * - * [options] appears to be no longer used, but we'll keep it in this interactor in case that - * turns out not to be true. - */ - fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) { - timeoutEvents.value = - KeyguardTimeoutWhileAwakeEvent( - timestamp = System.currentTimeMillis(), - options = options, - ) - } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt new file mode 100644 index 000000000000..9ed53ea74316 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.annotation.SuppressLint +import android.os.Bundle +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch + +/** + * Emitted when we receive a [KeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout] + * call. + */ +data class KeyguardLockNowEvent(val options: Bundle?) + +/** + * Logic around requests by [KeyguardService] to lock the device right now, even though the device + * is awake and not going to sleep. + * + * This can happen if WM#lockNow() is called, or if the screen is forced to stay awake but the lock + * timeout elapses. + * + * This is not the only way for the device to lock while the screen is on. The other cases, which do + * not directly involve [KeyguardService], are handled in [KeyguardLockWhileAwakeInteractor]. + */ +@SysUISingleton +class KeyguardServiceLockNowInteractor +@Inject +constructor(@Background val backgroundScope: CoroutineScope) { + + /** + * Emits whenever [KeyguardService] receives a call that indicates we should lock the device + * right now, even though the device is awake and not going to sleep. + * + * WARNING: This is only one of multiple reasons the device might need to lock while not going + * to sleep. Unless you're dealing with keyguard internals that specifically need to know that + * we're locking due to a call to doKeyguardTimeout, use + * [KeyguardLockWhileAwakeInteractor.lockWhileAwakeEvents]. + * + * This is fundamentally an event flow, hence the SharedFlow. + */ + @SuppressLint("SharedFlowCreation") + val lockNowEvents: MutableSharedFlow<KeyguardLockNowEvent> = MutableSharedFlow() + + /** + * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that + * the device locked while the screen was on. + * + * [options] appears to be no longer used, but we'll keep it in this interactor in case that + * turns out not to be true. + */ + fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) { + backgroundScope.launch { lockNowEvents.emit(KeyguardLockNowEvent(options = options)) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt index fbc7e2aecbdf..8641dfa5a183 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt @@ -25,6 +25,7 @@ import android.content.Intent import android.content.IntentFilter import android.provider.Settings import android.provider.Settings.Secure +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.widget.LockPatternUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -48,20 +49,21 @@ import javax.inject.Inject import kotlin.math.max import kotlin.math.min import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.map -import com.android.app.tracing.coroutines.launchTraced as launch +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onStart /** * Logic related to the ability to wake directly to GONE from asleep (AOD/DOZING), without going * through LOCKSCREEN or a BOUNCER state. * * This is possible in the following scenarios: - * - The lockscreen is disabled, either from an app request (SUW does this), or by the security + * - The keyguard is not enabled, either from an app request (SUW does this), or by the security * "None" setting. + * - The keyguard was suppressed via adb. * - A biometric authentication event occurred while we were asleep (fingerprint auth, etc). This * specifically is referred to throughout the codebase as "wake and unlock". * - The screen timed out, but the "lock after screen timeout" duration has not elapsed. @@ -86,43 +88,44 @@ constructor( private val lockPatternUtils: LockPatternUtils, private val systemSettings: SystemSettings, private val selectedUserInteractor: SelectedUserInteractor, + keyguardEnabledInteractor: KeyguardEnabledInteractor, + keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor, ) { /** - * Whether the lockscreen was disabled as of the last wake/sleep event, according to - * LockPatternUtils. - * - * This will always be true if [repository.isKeyguardServiceEnabled]=false, but it can also be - * true when the keyguard service is enabled if the lockscreen has been disabled via adb using - * the `adb shell locksettings set-disabled true` command, which is often done in tests. - * - * Unlike keyguardServiceEnabled, changes to this value should *not* immediately show or hide - * the keyguard. If the lockscreen is disabled in this way, it will just not show on the next - * sleep/wake. + * Whether the keyguard was suppressed as of the most recent wakefulness event or lockNow + * command. Keyguard suppression can only be queried (there is no callback available), and + * legacy code only queried the value in onStartedGoingToSleep and doKeyguardTimeout. Tests now + * depend on that behavior, so for now, we'll replicate it here. */ - private val isLockscreenDisabled: Flow<Boolean> = - powerInteractor.isAwake.map { isLockscreenDisabled() } + private val shouldSuppressKeyguard = + merge(powerInteractor.isAwake, keyguardServiceLockNowInteractor.lockNowEvents) + .map { keyguardEnabledInteractor.isKeyguardSuppressed() } + // Default to false, so that flows that combine this one emit prior to the first + // wakefulness emission. + .onStart { emit(false) } /** * Whether we can wake from AOD/DOZING directly to GONE, bypassing LOCKSCREEN/BOUNCER states. * * This is possible in the following cases: * - Keyguard is disabled, either from an app request or from security being set to "None". + * - Keyguard is suppressed, via adb locksettings. * - We're wake and unlocking (fingerprint auth occurred while asleep). * - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing. */ val canWakeDirectlyToGone = combine( repository.isKeyguardEnabled, - isLockscreenDisabled, + shouldSuppressKeyguard, repository.biometricUnlockState, repository.canIgnoreAuthAndReturnToGone, ) { keyguardEnabled, - isLockscreenDisabled, + shouldSuppressKeyguard, biometricUnlockState, canIgnoreAuthAndReturnToGone -> - (!keyguardEnabled || isLockscreenDisabled) || + (!keyguardEnabled || shouldSuppressKeyguard) || BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) || canIgnoreAuthAndReturnToGone } @@ -186,9 +189,9 @@ constructor( .sample( transitionInteractor.isCurrentlyIn( Scenes.Gone, - stateWithoutSceneContainer = KeyguardState.GONE + stateWithoutSceneContainer = KeyguardState.GONE, ), - ::Pair + ::Pair, ) .collect { (wakefulness, finishedInGone) -> // Save isAwake for use in onDreamingStarted/onDreamingStopped. @@ -260,7 +263,7 @@ constructor( delayedActionFilter, SYSTEMUI_PERMISSION, null /* scheduler */, - Context.RECEIVER_EXPORTED_UNAUDITED + Context.RECEIVER_EXPORTED_UNAUDITED, ) } @@ -282,7 +285,7 @@ constructor( context, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) val time = systemClock.elapsedRealtime() + getCanIgnoreAuthAndReturnToGoneDuration() @@ -311,16 +314,6 @@ constructor( } /** - * Returns whether the lockscreen is disabled, either because the keyguard service is disabled - * or because an adb command has disabled the lockscreen. - */ - private fun isLockscreenDisabled( - userId: Int = selectedUserInteractor.getSelectedUserId() - ): Boolean { - return lockPatternUtils.isLockScreenDisabled(userId) - } - - /** * Returns the duration within which we can return to GONE without auth after a screen timeout * (or power button press, if lock instantly is disabled). * @@ -336,7 +329,7 @@ constructor( .getIntForUser( Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, KEYGUARD_CAN_IGNORE_AUTH_DURATION, - userId + userId, ) .toLong() @@ -352,7 +345,7 @@ constructor( .getIntForUser( Settings.System.SCREEN_OFF_TIMEOUT, KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT, - userId + userId, ) .toLong() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt index f0bccacadc57..85ce5cd0f9fd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt @@ -23,7 +23,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -36,9 +38,7 @@ import kotlinx.coroutines.flow.Flow @SysUISingleton class AlternateBouncerToPrimaryBouncerTransitionViewModel @Inject -constructor( - animationFlow: KeyguardTransitionAnimationFlow, -) : DeviceEntryIconTransition { +constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow .setup( @@ -46,9 +46,23 @@ constructor( edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Bouncer), ) .setupWithoutSceneContainer( - edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER), + edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER) ) + private val alphaForAnimationStep: (Float) -> Float = + when { + SceneContainerFlag.isEnabled -> { step -> + 1f - Math.min((step / TO_BOUNCER_FADE_FRACTION), 1f) + } + else -> { step -> 1f - step } + } + + val lockscreenAlpha: Flow<Float> = + transitionAnimation.sharedFlow( + duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, + onStep = alphaForAnimationStep, + ) + override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java index b69b25dbddf2..8fbbb8bc4872 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java @@ -813,8 +813,15 @@ public class MediaSwitchingController } private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) { + boolean isSelectedDeviceNotAGroup = getSelectedMediaDevice().size() == 1; + if (enableInputRouting()) { + // When input routing is enabled, there are expected to be at least 2 total selected + // devices: one output device and one input device. + isSelectedDeviceNotAGroup = getSelectedMediaDevice().size() <= 2; + } + // Attach "Connect a device" item only when current output is not remote and not a group - if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) { + if (!isCurrentConnectedDeviceRemote() && isSelectedDeviceNotAGroup) { mediaItems.add(MediaItem.createPairNewDeviceMediaItem()); } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java index a3b7590117c1..d2b1d5449aaf 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java @@ -56,7 +56,7 @@ import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode; import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.AutoHideControllerStore; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.Utils; @@ -124,7 +124,7 @@ public class NavigationBarControllerImpl implements TaskbarDelegate taskbarDelegate, NavigationBarComponent.Factory navigationBarComponentFactory, DumpManager dumpManager, - AutoHideController autoHideController, + AutoHideControllerStore autoHideControllerStore, LightBarController lightBarController, TaskStackChangeListeners taskStackChangeListeners, Optional<Pip> pipOptional, @@ -146,8 +146,9 @@ public class NavigationBarControllerImpl implements mTaskbarDelegate = taskbarDelegate; mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService, navBarHelper, navigationModeController, sysUiFlagsContainer, - dumpManager, autoHideController, lightBarController, pipOptional, - backAnimation.orElse(null), taskStackChangeListeners); + dumpManager, autoHideControllerStore.forDisplay(mContext.getDisplayId()), + lightBarController, pipOptional, backAnimation.orElse(null), + taskStackChangeListeners); mIsLargeScreen = isLargeScreen(mContext); mIsPhone = determineIfPhone(mContext, deviceStateManager); dumpManager.registerDumpable(this); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java index 40613c0edc68..c895732f79f6 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java @@ -149,9 +149,9 @@ import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.data.repository.LightBarControllerStore; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.AutoHideControllerStore; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -259,10 +259,10 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private boolean mTransientShownFromGestureOnSystemBar; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; private LightBarController mLightBarController; - private final LightBarControllerStore mLightBarControllerStore; + private final LightBarController mMainLightBarController; + private final LightBarController.Factory mLightBarControllerFactory; private AutoHideController mAutoHideController; - private final AutoHideController mMainAutoHideController; - private final AutoHideController.Factory mAutoHideControllerFactory; + private final AutoHideControllerStore mAutoHideControllerStore; private final Optional<TelecomManager> mTelecomManagerOptional; private final InputMethodManager mInputMethodManager; private final TaskStackChangeListeners mTaskStackChangeListeners; @@ -580,9 +580,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements @Background Executor bgExecutor, UiEventLogger uiEventLogger, NavBarHelper navBarHelper, - LightBarControllerStore lightBarControllerStore, - AutoHideController mainAutoHideController, - AutoHideController.Factory autoHideControllerFactory, + LightBarController mainLightBarController, + LightBarController.Factory lightBarControllerFactory, + AutoHideControllerStore autoHideControllerStore, Optional<TelecomManager> telecomManagerOptional, InputMethodManager inputMethodManager, DeadZone deadZone, @@ -627,9 +627,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mUiEventLogger = uiEventLogger; mNavBarHelper = navBarHelper; mNotificationShadeDepthController = notificationShadeDepthController; - mLightBarControllerStore = lightBarControllerStore; - mMainAutoHideController = mainAutoHideController; - mAutoHideControllerFactory = autoHideControllerFactory; + mMainLightBarController = mainLightBarController; + mLightBarControllerFactory = lightBarControllerFactory; + mAutoHideControllerStore = autoHideControllerStore; mTelecomManagerOptional = telecomManagerOptional; mInputMethodManager = inputMethodManager; mUserContextProvider = userContextProvider; @@ -840,16 +840,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements // Unfortunately, we still need it because status bar needs LightBarController // before notifications creation. We cannot directly use getLightBarController() // from NavigationBarFragment directly. - LightBarController lightBarController = mLightBarControllerStore.forDisplay(mDisplayId); + LightBarController lightBarController = mIsOnDefaultDisplay + ? mMainLightBarController : mLightBarControllerFactory.create(mContext); setLightBarController(lightBarController); - // TODO(b/118592525): to support multi-display, we start to add something which is - // per-display, while others may be global. I think it's time to - // add a new class maybe named DisplayDependency to solve - // per-display Dependency problem. - // Alternative: this is a good case for a Dagger subcomponent. Same with LightBarController. - AutoHideController autoHideController = mIsOnDefaultDisplay - ? mMainAutoHideController : mAutoHideControllerFactory.create(mContext); + AutoHideController autoHideController = mAutoHideControllerStore.forDisplay(mDisplayId); setAutoHideController(autoHideController); restoreAppearanceAndTransientState(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 5167d173647e..9dc21fb89b16 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -629,11 +629,7 @@ constructor( id = R.string.accessibility_quick_settings_expand ) ) - .padding( - horizontal = { - QuickSettingsShade.Dimensions.Padding.roundToPx() - } - ) + .padding(horizontal = qsHorizontalMargin()) ) { QuickQuickSettingsLayout( tiles = Tiles, @@ -737,8 +733,8 @@ constructor( .sysuiResTag(ResIdTags.quickSettingsPanel) .padding( top = QuickSettingsShade.Dimensions.Padding, - start = QuickSettingsShade.Dimensions.Padding, - end = QuickSettingsShade.Dimensions.Padding, + start = qsHorizontalMargin(), + end = qsHorizontalMargin(), ) ) { QuickSettingsLayout( @@ -1127,6 +1123,8 @@ private object ResIdTags { const val qsFooterActions = "qs_footer_actions" } +@Composable private fun qsHorizontalMargin() = dimensionResource(id = R.dimen.qs_horizontal_margin) + @Composable private fun interactionsConfig() = InteractionsConfig( diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt index ca28ab3e6ce3..2928ad117922 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt @@ -75,6 +75,8 @@ fun SceneScope.QuickQuickSettings( coroutineScope = scope, bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), tileHapticsViewModelFactoryProvider = viewModel.tileHapticsViewModelFactoryProvider, + // There should be no QuickQuickSettings when the details view is enabled. + detailsViewModel = null, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt new file mode 100644 index 000000000000..1bfbbe1964dc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.compose + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em +import com.android.systemui.qs.flags.QsDetailedView +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel + +@Composable +fun TileDetails(detailsViewModel: DetailsViewModel) { + + if (!QsDetailedView.isEnabled) { + throw IllegalStateException("QsDetailedView should be enabled") + } + + val tileDetailedViewModel = detailsViewModel.activeTileDetails ?: return + + DisposableEffect(Unit) { onDispose { detailsViewModel.closeDetailedView() } } + + Column( + modifier = Modifier + .fillMaxWidth() + // The height of the details view is TBD. + .fillMaxHeight() + ) { + CompositionLocalProvider( + value = LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + + IconButton( + onClick = { detailsViewModel.closeDetailedView() }, + modifier = Modifier + .align(Alignment.CenterVertically) + .height(TileDetailsDefaults.IconHeight) + .padding(start = TileDetailsDefaults.IconPadding), + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + // Description is TBD + contentDescription = "Back to QS panel", + ) + } + Text( + text = tileDetailedViewModel.getTitle(), + modifier = Modifier + .align(Alignment.CenterVertically), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleLarge + ) + IconButton( + onClick = { tileDetailedViewModel.clickOnSettingsButton() }, + modifier = Modifier + .align(Alignment.CenterVertically) + .height(TileDetailsDefaults.IconHeight) + .padding(end = TileDetailsDefaults.IconPadding), + ) { + Icon( + imageVector = Icons.Default.Settings, + // Description is TBD + contentDescription = "Go to Settings", + ) + } + } + Text( + text = tileDetailedViewModel.getSubTitle(), + modifier = Modifier + .fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleSmall + + ) + } + tileDetailedViewModel.GetContentView() + } +} + +private object TileDetailsDefaults { + val IconHeight = 48.dp + val IconPadding = 4.dp +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index cb857ae5efc6..8fd99a52eceb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt @@ -36,6 +36,7 @@ import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.rememberEditListState import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridViewModel @@ -49,6 +50,7 @@ import javax.inject.Inject class InfiniteGridLayout @Inject constructor( + private val detailsViewModel: DetailsViewModel, private val iconTilesViewModel: IconTilesViewModel, private val viewModelFactory: InfiniteGridViewModel.Factory, private val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider, @@ -100,6 +102,7 @@ constructor( tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider, coroutineScope = scope, bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), + detailsViewModel = detailsViewModel, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt index 0a80a19871fd..abdf923ebe73 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt @@ -77,10 +77,12 @@ import com.android.systemui.haptics.msdl.qs.TileHapticsViewModel import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.flags.QsDetailedView import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.panels.ui.viewmodel.toUiState @@ -121,6 +123,7 @@ fun Tile( bounceableInfo: BounceableInfo, tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider, modifier: Modifier = Modifier, + detailsViewModel: DetailsViewModel?, ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) val currentBounceableInfo by rememberUpdatedState(bounceableInfo) @@ -163,12 +166,20 @@ fun Tile( .takeIf { uiState.handlesLongClick } TileContainer( onClick = { - tile.onClick(expandable) - hapticsViewModel?.setTileInteractionState( - TileHapticsViewModel.TileInteractionState.CLICKED - ) - if (uiState.accessibilityUiState.toggleableState != null) { - coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + var hasDetails = false + if (QsDetailedView.isEnabled) { + hasDetails = detailsViewModel?.onTileClicked(tile.spec) == true + } + if (!hasDetails) { + // For those tile's who doesn't have a detailed view, process with their + // `onClick` behavior. + tile.onClick(expandable) + hapticsViewModel?.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.CLICKED + ) + if (uiState.accessibilityUiState.toggleableState != null) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } } }, onLongClick = longClick, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt index b27c08077834..1113053e36d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt @@ -73,6 +73,11 @@ class ResizingState(tileSpec: TileSpec, startsAsIcon: Boolean) { anchoredDraggableState.animateTo(if (isIcon) QSDragAnchor.Icon else QSDragAnchor.Large) } + suspend fun toggleCurrentValue() { + val isIcon = anchoredDraggableState.currentValue == QSDragAnchor.Icon + updateCurrentValue(!isIcon) + } + fun progress(): Float = anchoredDraggableState.progress(QSDragAnchor.Icon, QSDragAnchor.Large) /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt index a187ff135cbb..c1545e1263db 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt @@ -21,6 +21,7 @@ import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring import androidx.compose.foundation.Canvas +import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.anchoredDraggable import androidx.compose.foundation.layout.Box @@ -32,6 +33,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent @@ -50,6 +52,7 @@ import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth import kotlin.math.roundToInt +import kotlinx.coroutines.launch /** * Places a dot to handle resizing drag events. Use this on tiles to resize. @@ -88,6 +91,7 @@ private fun ResizingHandle(enabled: Boolean, state: ResizingState, modifier: Mod // Manually creating the touch target around the resizing dot to ensure that the next tile // does not receive the touch input accidentally. val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current + val scope = rememberCoroutineScope() Box( modifier .layout { measurable, constraints -> @@ -106,6 +110,9 @@ private fun ResizingHandle(enabled: Boolean, state: ResizingState, modifier: Mod state = state.anchoredDraggableState, orientation = Orientation.Horizontal, ) + .clickable(enabled = enabled, interactionSource = null, indication = null) { + scope.launch { state.toggleCurrentValue() } + } ) { ResizingDot(enabled = enabled, modifier = Modifier.align(Alignment.Center)) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt new file mode 100644 index 000000000000..d8361f544bdc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.viewmodel + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.plugins.qs.TileDetailsViewModel +import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor +import com.android.systemui.qs.pipeline.shared.TileSpec +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +@SysUISingleton +class DetailsViewModel +@Inject constructor(val currentTilesInteractor: CurrentTilesInteractor) { + + /** + * The current active [TileDetailsViewModel]. If it's `null`, it means the qs overlay is not + * showing any details view. It changes when [onTileClicked] and [closeDetailedView]. + */ + private val _activeTileDetails = mutableStateOf<TileDetailsViewModel?>(null) + val activeTileDetails by _activeTileDetails + + /** + * Update the active [TileDetailsViewModel] to `null`. + * @see activeTileDetails + */ + fun closeDetailedView() { + _activeTileDetails.value = null + } + + /** + * Update the active [TileDetailsViewModel] to the `spec`'s corresponding view model. + * Return if the [TileDetailsViewModel] is successfully found. + * @see activeTileDetails + */ + fun onTileClicked(spec: TileSpec?): Boolean { + if (spec == null) { + _activeTileDetails.value = null + return false + } + + _activeTileDetails.value = currentTilesInteractor + .currentQSTiles + .firstOrNull { it.tileSpec == spec.spec } + ?.detailsViewModel + + return _activeTileDetails.value != null + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt index 42ef0cd3abb7..7225800e25e3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt @@ -28,11 +28,14 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.plugins.qs.TileDetailsViewModel import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.flags.QsDetailedView import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel import com.android.systemui.qs.tiles.dialog.InternetDialogManager import com.android.systemui.qs.tiles.dialog.WifiStateWorker import com.android.systemui.res.R @@ -90,6 +93,9 @@ constructor( } override fun handleClick(expandable: Expandable?) { + if (QsDetailedView.isEnabled) { + return + } mainHandler.post { internetDialogManager.create( aboveStatusBar = true, @@ -100,6 +106,10 @@ constructor( } } + override fun getDetailsViewModel(): TileDetailsViewModel { + return InternetDetailsViewModel { longClick(null) } + } + override fun secondaryClick(expandable: Expandable?) { // TODO(b/358352265): Figure out the correct action for the secondary click // Toggle wifi diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt new file mode 100644 index 000000000000..f239a179d79a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.dialog + +import android.view.LayoutInflater +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.android.systemui.plugins.qs.TileDetailsViewModel +import com.android.systemui.res.R + +class InternetDetailsViewModel( + onLongClick: () -> Unit, +) : TileDetailsViewModel() { + private val _onLongClick = onLongClick + + @Composable + override fun GetContentView() { + AndroidView( + modifier = Modifier.fillMaxWidth().fillMaxHeight(), + factory = { context -> + // Inflate with the existing dialog xml layout + LayoutInflater.from(context) + .inflate(R.layout.internet_connectivity_dialog, null) + // TODO: b/377388104 - Implement the internet details view + }, + ) + } + + override fun clickOnSettingsButton() { + _onLongClick() + } + + override fun getTitle(): String { + // TODO: b/377388104 Update the placeholder text + return "Internet" + } + + override fun getSubTitle(): String { + // TODO: b/377388104 Update the placeholder text + return "Tab a network to connect" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt index 8f870d468997..4806c3f83224 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.custom.domain.interactor import android.os.UserHandle import android.service.quicksettings.Tile -import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePacka import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel import com.android.systemui.qs.tiles.impl.di.QSTileScope import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -44,7 +45,6 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn -import com.android.app.tracing.coroutines.launchTraced as launch @QSTileScope @OptIn(ExperimentalCoroutinesApi::class) @@ -64,7 +64,7 @@ constructor( private val bindingFlow = mutableUserFlow .flatMapLatest { user -> - ConflatedCallbackFlow.conflatedCallbackFlow { + conflatedCallbackFlow { serviceInteractor.setUser(user) // Wait for the CustomTileInteractor to become initialized first, because @@ -79,7 +79,7 @@ constructor( defaultsRepository.requestNewDefaults( user, tileSpec.componentName, - true + true, ) } .launchIn(this) @@ -99,7 +99,7 @@ constructor( override fun tileData( user: UserHandle, - triggers: Flow<DataUpdateTrigger> + triggers: Flow<DataUpdateTrigger>, ): Flow<CustomTileDataModel> { tileScope.launch { mutableUserFlow.emit(user) } return bindingFlow.combine(triggers) { _, _ -> }.flatMapLatest { dataFlow(user) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt index ab3862b75ee8..f9a1ad5d8424 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt @@ -25,6 +25,7 @@ import com.android.systemui.Dumpable import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.QSHost import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon @@ -35,14 +36,17 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.io.PrintWriter import java.util.concurrent.CopyOnWriteArraySet +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectIndexed import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.launch // TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout class QSTileViewModelAdapter @@ -51,6 +55,7 @@ constructor( @Application private val applicationScope: CoroutineScope, private val qsHost: QSHost, @Assisted private val qsTileViewModel: QSTileViewModel, + @UiBackground private val uiBgDispatcher: CoroutineDispatcher, ) : QSTile, Dumpable { private val context @@ -162,19 +167,25 @@ constructor( override fun setListening(client: Any?, listening: Boolean) { client ?: return if (listening) { - val clientWasNotAlreadyListening = listeningClients.add(client) - if (clientWasNotAlreadyListening && listeningClients.size == 1) { - stateJob = - qsTileViewModel.state - .filterNotNull() - .map { mapState(context, it, qsTileViewModel.config) } - .onEach { legacyState -> - val changed = legacyState.copyTo(cachedState) - if (changed) { - callbacks.forEach { it.onStateChanged(legacyState) } + applicationScope.launch(uiBgDispatcher) { + val shouldStartMappingJob = + listeningClients.add(client) // new client + && listeningClients.size == 1 // first client + + if (shouldStartMappingJob) { + stateJob = + qsTileViewModel.state + .filterNotNull() + .map { mapState(context, it, qsTileViewModel.config) } + .onEach { legacyState -> + val changed = legacyState.copyTo(cachedState) + if (changed) { + callbacks.forEach { it.onStateChanged(legacyState) } + } } - } - .launchIn(applicationScope) + .flowOn(uiBgDispatcher) + .launchIn(applicationScope) + } } } else { listeningClients.remove(client) diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt index da175c9120ba..62b120332289 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel @@ -36,6 +37,7 @@ constructor( @Assisted supportsBrightnessMirroring: Boolean, val tileGridViewModel: TileGridViewModel, val editModeViewModel: EditModeViewModel, + val detailsViewModel: DetailsViewModel, ) : ExclusiveActivatable() { val brightnessSliderViewModel = diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt index ba789a01d285..3a07ce959bb3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.domain.interactor +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton @@ -29,9 +30,9 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.init.NotificationsController import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor -import com.android.systemui.statusbar.policy.HeadsUpManager import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineScope @@ -44,7 +45,6 @@ import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import com.android.app.tracing.coroutines.launchTraced as launch /** Business logic about the visibility of various parts of the window root view. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt index b8ea8f9052ca..c5c705c97b8d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt @@ -29,6 +29,7 @@ import android.view.View import android.view.ViewGroup import android.view.WindowInsets import android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL +import android.view.accessibility.AccessibilityEvent import android.widget.FrameLayout import android.widget.ImageView import com.android.systemui.res.R @@ -83,6 +84,20 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) : }) gestureDetector.setIsLongpressEnabled(false) + + // Extend the timeout on any accessibility event (e.g. voice access or explore-by-touch). + setAccessibilityDelegate( + object : AccessibilityDelegate() { + override fun onRequestSendAccessibilityEvent( + host: ViewGroup, + child: View, + event: AccessibilityEvent, + ): Boolean { + userInteractionCallback?.invoke() + return super.onRequestSendAccessibilityEvent(host, child, event) + } + } + ) } override fun onFinishInflate() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 49ceba834dd4..31780a56f7f0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -37,9 +37,11 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.theme.PlatformTheme import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Flags +import com.android.systemui.Flags.communalHubOnMobile import com.android.systemui.ambient.touch.TouchMonitor import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.communal.dagger.Communal @@ -70,7 +72,6 @@ import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import com.android.app.tracing.coroutines.launchTraced as launch /** * Controller that's responsible for the glanceable hub container view and its touch handling. @@ -513,14 +514,19 @@ constructor( val touchOnUmo = keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt()) val touchOnSmartspace = lockscreenSmartspaceController.isWithinSmartspaceBounds(ev.x.toInt(), ev.y.toInt()) - if (!hubShowing && (touchOnNotifications || touchOnUmo || touchOnSmartspace)) { + val glanceableHubV2 = communalHubOnMobile() + if ( + !hubShowing && + (touchOnNotifications || touchOnUmo || touchOnSmartspace || glanceableHubV2) + ) { logger.d({ "Lockscreen touch ignored: touchOnNotifications: $bool1, touchOnUmo: $bool2, " + - "touchOnSmartspace: $bool3" + "touchOnSmartspace: $bool3, glanceableHubV2: $bool4" }) { bool1 = touchOnNotifications bool2 = touchOnUmo bool3 = touchOnSmartspace + bool4 = glanceableHubV2 } return false } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 42a756c27de7..88522d559c30 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -186,13 +186,15 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.ConversationNotificationManager; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor; import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -217,17 +219,15 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.util.Compile; @@ -298,7 +298,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump * The minimum scale to "squish" the Shade and associated elements down to, for Back gesture */ public static final float SHADE_BACK_ANIM_MIN_SCALE = 0.9f; - private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + private final ShadeTouchableRegionManager mShadeTouchableRegionManager; private final Resources mResources; private final KeyguardStateController mKeyguardStateController; private final SysuiStatusBarStateController mStatusBarStateController; @@ -695,7 +695,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ShadeLogger shadeLogger, @ShadeDisplayAware ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, - StatusBarTouchableRegionManager statusBarTouchableRegionManager, + ShadeTouchableRegionManager shadeTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager, @@ -840,7 +840,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mVibratorHelper = vibratorHelper; mMSDLPlayer = msdlPlayer; mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation); - mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; + mShadeTouchableRegionManager = shadeTouchableRegionManager; mSystemClock = systemClock; mKeyguardMediaController = keyguardMediaController; mMetricsLogger = metricsLogger; @@ -1551,7 +1551,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private Rect calculateGestureExclusionRect() { Rect exclusionRect = null; - Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion(); + Region touchableRegion = mShadeTouchableRegionManager.calculateTouchableRegion(); if (isFullyCollapsed() && touchableRegion != null) { // Note: The manager also calculates the non-pinned touchable region exclusionRect = touchableRegion.getBounds(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index 04f89be97e0a..0df2299eb8dd 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -98,7 +98,7 @@ import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.SplitShadeStateController; @@ -141,7 +141,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final NotificationShadeDepthController mDepthController; private final ShadeHeaderController mShadeHeaderController; - private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + private final ShadeTouchableRegionManager mShadeTouchableRegionManager; private final Provider<StatusBarLongPressGestureDetector> mStatusBarLongPressGestureDetector; private final KeyguardStateController mKeyguardStateController; private final KeyguardBypassController mKeyguardBypassController; @@ -317,7 +317,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum LockscreenShadeTransitionController lockscreenShadeTransitionController, NotificationShadeDepthController notificationShadeDepthController, ShadeHeaderController shadeHeaderController, - StatusBarTouchableRegionManager statusBarTouchableRegionManager, + ShadeTouchableRegionManager shadeTouchableRegionManager, Provider<StatusBarLongPressGestureDetector> statusBarLongPressGestureDetector, KeyguardStateController keyguardStateController, KeyguardBypassController keyguardBypassController, @@ -366,7 +366,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mDepthController = notificationShadeDepthController; mShadeHeaderController = shadeHeaderController; - mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; + mShadeTouchableRegionManager = shadeTouchableRegionManager; mStatusBarLongPressGestureDetector = statusBarLongPressGestureDetector; mKeyguardStateController = keyguardStateController; mKeyguardBypassController = keyguardBypassController; @@ -695,7 +695,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(), /* bottom= */ headerBottom + frameTop); // Also allow QS to intercept if the touch is near the notch. - mStatusBarTouchableRegionManager.updateRegionForNotch(mInterceptRegion); + mShadeTouchableRegionManager.updateRegionForNotch(mInterceptRegion); final boolean onHeader = mInterceptRegion.contains((int) x, (int) y); if (getExpanded()) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index 0b36e685d914..91ca2ca52287 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -17,9 +17,10 @@ package com.android.systemui.shade import android.content.Context -import android.content.MutableContextWrapper import android.content.res.Resources import android.view.LayoutInflater +import android.view.WindowManager +import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE import com.android.systemui.CoreStartable import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.ConfigurationStateImpl @@ -29,9 +30,12 @@ import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImp import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R import com.android.systemui.scene.ui.view.WindowRootView +import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl +import com.android.systemui.shade.display.ShadeDisplayPolicyModule import com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround import com.android.systemui.statusbar.phone.ConfigurationControllerImpl @@ -56,7 +60,7 @@ import javax.inject.Provider * By using this dedicated module, we ensure the notification shade window always utilizes the * correct display context and resources, regardless of the display it's on. */ -@Module(includes = [OptionalShadeDisplayAwareBindings::class]) +@Module(includes = [OptionalShadeDisplayAwareBindings::class, ShadeDisplayPolicyModule::class]) object ShadeDisplayAwareModule { /** Creates a new context for the shade window. */ @@ -65,7 +69,9 @@ object ShadeDisplayAwareModule { @SysUISingleton fun provideShadeDisplayAwareContext(context: Context): Context { return if (ShadeWindowGoesAround.isEnabled) { - MutableContextWrapper(context) + context + .createWindowContext(context.display, TYPE_NOTIFICATION_SHADE, /* options= */ null) + .apply { setTheme(R.style.Theme_SystemUI) } } else { context } @@ -74,6 +80,20 @@ object ShadeDisplayAwareModule { @Provides @ShadeDisplayAware @SysUISingleton + fun provideShadeWindowManager( + defaultWindowManager: WindowManager, + @ShadeDisplayAware context: Context, + ): WindowManager { + return if (ShadeWindowGoesAround.isEnabled) { + context.getSystemService(WindowManager::class.java) as WindowManager + } else { + defaultWindowManager + } + } + + @Provides + @ShadeDisplayAware + @SysUISingleton fun provideShadeDisplayAwareResources(@ShadeDisplayAware context: Context): Resources { return context.resources } @@ -163,6 +183,15 @@ object ShadeDisplayAwareModule { return impl } + @SysUISingleton + @Provides + fun provideMutableShadePositionRepository( + impl: ShadeDisplaysRepositoryImpl + ): MutableShadeDisplaysRepository { + ShadeWindowGoesAround.isUnexpectedlyInLegacyMode() + return impl + } + @Provides @IntoMap @ClassKey(ShadePrimaryDisplayCommand::class) diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt index a5d9e9660ca5..a54f6b9c6743 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt @@ -20,11 +20,14 @@ import android.view.Display import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.display.data.repository.DisplayRepository -import com.android.systemui.shade.data.repository.ShadeDisplaysRepository +import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository +import com.android.systemui.shade.display.ShadeDisplayPolicy +import com.android.systemui.shade.display.SpecificDisplayIdPolicy import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter import javax.inject.Inject +import kotlin.text.toIntOrNull @SysUISingleton class ShadePrimaryDisplayCommand @@ -32,7 +35,9 @@ class ShadePrimaryDisplayCommand constructor( private val commandRegistry: CommandRegistry, private val displaysRepository: DisplayRepository, - private val positionRepository: ShadeDisplaysRepository, + private val positionRepository: MutableShadeDisplaysRepository, + private val policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>, + private val defaultPolicy: ShadeDisplayPolicy, ) : Command, CoreStartable { override fun start() { @@ -40,8 +45,11 @@ constructor( } override fun help(pw: PrintWriter) { - pw.println("shade_display_override <displayId> ") - pw.println("Set the display which is holding the shade.") + pw.println("shade_display_override (<displayId>|<policyName>) ") + pw.println("Set the display which is holding the shade, or the policy that defines it.") + pw.println() + pw.println("shade_display_override policies") + pw.println("Lists available policies") pw.println() pw.println("shade_display_override reset ") pw.println("Reset the display which is holding the shade.") @@ -68,21 +76,27 @@ constructor( "reset" -> reset() "list", "status" -> printStatus() + "policies" -> printPolicies() "any_external" -> anyExternal() - else -> { - val cmdAsInteger = command?.toIntOrNull() - if (cmdAsInteger != null) { - changeDisplay(displayId = cmdAsInteger) - } else { - help(pw) - } + null -> help(pw) + else -> parsePolicy(command) + } + } + + private fun parsePolicy(policyIdentifier: String) { + val displayId = policyIdentifier.toIntOrNull() + when { + displayId != null -> changeDisplay(displayId = displayId) + policies.any { it.name == policyIdentifier } -> { + positionRepository.policy.value = policies.first { it.name == policyIdentifier } } + else -> help(pw) } } private fun reset() { - positionRepository.resetDisplayId() - pw.println("Reset shade primary display id to ${Display.DEFAULT_DISPLAY}") + positionRepository.policy.value = defaultPolicy + pw.println("Reset shade display policy to default policy: ${defaultPolicy.name}") } private fun printStatus() { @@ -95,6 +109,15 @@ constructor( } } + private fun printPolicies() { + val currentPolicyName = positionRepository.policy.value.name + pw.println("Available policies: ") + policies.forEach { + pw.print(" - ${it.name}") + pw.println(if (currentPolicyName == it.name) " (Current policy)" else "") + } + } + private fun anyExternal() { val anyExternalDisplay = displaysRepository.displays.value.firstOrNull { @@ -116,7 +139,7 @@ constructor( } private fun setDisplay(id: Int) { - positionRepository.setDisplayId(id) + positionRepository.policy.value = SpecificDisplayIdPolicy(id) pw.println("New shade primary display id is $id") } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt index 7346a28dc7a7..9ca23f0f1e17 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt @@ -19,8 +19,8 @@ import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor import com.android.systemui.statusbar.GestureRecorder +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.policy.HeadsUpManager /** * Allows CentralSurfacesImpl to interact with the shade. Only CentralSurfacesImpl should reference @@ -37,7 +37,7 @@ interface ShadeSurface : centralSurfaces: CentralSurfaces, recorder: GestureRecorder, hideExpandedRunnable: Runnable, - headsUpManager: HeadsUpManager + headsUpManager: HeadsUpManager, ) /** Cancels any pending collapses. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt index ec4018c7d238..3bbda162cf31 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt @@ -17,8 +17,8 @@ package com.android.systemui.shade import com.android.systemui.statusbar.GestureRecorder +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.policy.HeadsUpManager import javax.inject.Inject class ShadeSurfaceImpl @Inject constructor() : ShadeSurface, ShadeViewControllerEmptyImpl() { @@ -26,7 +26,7 @@ class ShadeSurfaceImpl @Inject constructor() : ShadeSurface, ShadeViewController centralSurfaces: CentralSurfaces, recorder: GestureRecorder, hideExpandedRunnable: Runnable, - headsUpManager: HeadsUpManager + headsUpManager: HeadsUpManager, ) {} override fun cancelPendingCollapse() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt index 71c565816362..732d4d1500e7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt @@ -23,14 +23,14 @@ import kotlinx.coroutines.flow.StateFlow class FakeShadeDisplayRepository : ShadeDisplaysRepository { private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY) - override fun setDisplayId(displayId: Int) { + fun setDisplayId(displayId: Int) { _displayId.value = displayId } override val displayId: StateFlow<Int> get() = _displayId - override fun resetDisplayId() { + fun resetDisplayId() { _displayId.value = Display.DEFAULT_DISPLAY } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt index 4a95e339cade..756241e9b071 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt @@ -18,37 +18,40 @@ package com.android.systemui.shade.data.repository import android.view.Display import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.shade.display.ShadeDisplayPolicy import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.stateIn +/** Source of truth for the display currently holding the shade. */ interface ShadeDisplaysRepository { /** ID of the display which currently hosts the shade */ val displayId: StateFlow<Int> +} - /** - * Updates the value of the shade display id stored, emitting to the new display id to every - * component dependent on the shade display id - */ - fun setDisplayId(displayId: Int) - - /** Resets value of shade primary display to the default display */ - fun resetDisplayId() +/** Allows to change the policy that determines in which display the Shade window is visible. */ +interface MutableShadeDisplaysRepository : ShadeDisplaysRepository { + /** Updates the policy to select where the shade is visible. */ + val policy: MutableStateFlow<ShadeDisplayPolicy> } -/** Source of truth for the display currently holding the shade. */ +/** Keeps the policy and propagates the display id for the shade from it. */ @SysUISingleton -class ShadeDisplaysRepositoryImpl @Inject constructor() : ShadeDisplaysRepository { - private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY) - - override val displayId: StateFlow<Int> - get() = _displayId - - override fun setDisplayId(displayId: Int) { - _displayId.value = displayId - } - - override fun resetDisplayId() { - _displayId.value = Display.DEFAULT_DISPLAY - } +@OptIn(ExperimentalCoroutinesApi::class) +class ShadeDisplaysRepositoryImpl +@Inject +constructor(defaultPolicy: ShadeDisplayPolicy, @Background bgScope: CoroutineScope) : + MutableShadeDisplaysRepository { + override val policy = MutableStateFlow<ShadeDisplayPolicy>(defaultPolicy) + + override val displayId: StateFlow<Int> = + policy + .flatMapLatest { it.displayId } + .stateIn(bgScope, SharingStarted.WhileSubscribed(), Display.DEFAULT_DISPLAY) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt new file mode 100644 index 000000000000..3f6c949fcba6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.display + +import android.view.Display +import android.view.Display.DEFAULT_DISPLAY +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.data.repository.DisplayRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +/** + * Returns an external display if one exists, otherwise the default display. + * + * If there are multiple external displays, the one with minimum display ID is returned. + */ +@SysUISingleton +class AnyExternalShadeDisplayPolicy +@Inject +constructor(displayRepository: DisplayRepository, @Background bgScope: CoroutineScope) : + ShadeDisplayPolicy { + override val name: String + get() = "any_external_display" + + override val displayId: StateFlow<Int> = + displayRepository.displays + .map { displays -> + displays + .filter { it.displayId != DEFAULT_DISPLAY && it.type in ALLOWED_DISPLAY_TYPES } + .minOfOrNull { it.displayId } ?: DEFAULT_DISPLAY + } + .stateIn(bgScope, SharingStarted.WhileSubscribed(), DEFAULT_DISPLAY) + + private companion object { + val ALLOWED_DISPLAY_TYPES = + setOf(Display.TYPE_EXTERNAL, Display.TYPE_OVERLAY, Display.TYPE_WIFI) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt new file mode 100644 index 000000000000..1b22ee40009b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.display + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet +import kotlinx.coroutines.flow.StateFlow + +/** Describes the display the shade should be shown in. */ +interface ShadeDisplayPolicy { + val name: String + + /** The display id the shade should be at, according to this policy. */ + val displayId: StateFlow<Int> +} + +@Module +interface ShadeDisplayPolicyModule { + @IntoSet + @Binds + fun provideDefaultPolicyToSet(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy + + @IntoSet + @Binds + fun provideAnyExternalShadeDisplayPolicyToSet( + impl: AnyExternalShadeDisplayPolicy + ): ShadeDisplayPolicy + + @Binds fun provideDefaultPolicy(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt new file mode 100644 index 000000000000..13e766409bab --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.display + +import android.view.Display +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +/** Policy to specify a display id explicitly. */ +open class SpecificDisplayIdPolicy(displayId: Int) : ShadeDisplayPolicy { + override val name: String + get() = "display_${displayId}_policy" + + override val displayId: StateFlow<Int> = MutableStateFlow(displayId) +} + +class DefaultShadeDisplayPolicy @Inject constructor() : + SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY) diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt index 432d5f553fbb..fb2cbec84236 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt @@ -16,28 +16,21 @@ package com.android.systemui.shade.domain.interactor -import android.content.ComponentCallbacks import android.content.Context -import android.content.MutableContextWrapper -import android.content.res.Configuration -import android.content.res.Resources import android.util.Log import android.view.WindowManager -import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE +import androidx.annotation.UiThread import com.android.app.tracing.coroutines.launchTraced import com.android.app.tracing.traceSection import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository -import com.android.systemui.display.shared.model.DisplayWindowProperties import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.ShadeWindowLayoutParams import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround -import com.android.systemui.statusbar.phone.ConfigurationForwarder import com.android.systemui.util.kotlin.getOrNull import java.util.Optional import javax.inject.Inject @@ -53,13 +46,14 @@ constructor( optionalShadeRootView: Optional<WindowRootView>, private val shadePositionRepository: ShadeDisplaysRepository, @ShadeDisplayAware private val shadeContext: Context, - @ShadeDisplayAware private val shadeResources: Resources, - private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, + @ShadeDisplayAware private val wm: WindowManager, @Background private val bgScope: CoroutineScope, - @ShadeDisplayAware private val shadeConfigurationForwarder: ConfigurationForwarder, @Main private val mainThreadContext: CoroutineContext, ) : CoreStartable { + private val shadeLayoutParams: WindowManager.LayoutParams = + ShadeWindowLayoutParams.create(shadeContext) + private val shadeRootView = optionalShadeRootView.getOrNull() ?: error( @@ -69,9 +63,6 @@ constructor( """ .trimIndent() ) - // TODO: b/362719719 - Get rid of this callback as the root view should automatically get the - // correct configuration once it's moved to another window. - private var unregisterConfigChangedCallbacks: (() -> Unit)? = null override fun start() { ShadeWindowGoesAround.isUnexpectedlyInLegacyMode() @@ -94,7 +85,7 @@ constructor( return } try { - moveShadeWindow(fromId = currentId, toId = destinationId) + withContext(mainThreadContext) { moveShadeWindow(toId = destinationId) } } catch (e: IllegalStateException) { Log.e( TAG, @@ -104,68 +95,26 @@ constructor( } } - private suspend fun moveShadeWindow(fromId: Int, toId: Int) { - val (_, _, _, sourceWm) = getDisplayWindowProperties(fromId) - val (_, _, destContext, destWm) = getDisplayWindowProperties(toId) - withContext(mainThreadContext) { - traceSection({ "MovingShadeWindow from $fromId to $toId" }) { - removeShade(sourceWm) - addShade(destWm) - overrideContextAndResources(newContext = destContext) - registerConfigurationChange(destContext) - } - traceSection("ShadeDisplaysInteractor#onConfigurationChanged") { - dispatchConfigurationChanged(destContext.resources.configuration) - } - } - } - - private fun removeShade(wm: WindowManager): Unit = - traceSection("removeView") { wm.removeView(shadeRootView) } - - private fun addShade(wm: WindowManager): Unit = - traceSection("addView") { - wm.addView(shadeRootView, ShadeWindowLayoutParams.create(shadeContext)) + @UiThread + private fun moveShadeWindow(toId: Int) { + traceSection({ "moveShadeWindow to $toId" }) { + removeShadeWindow() + updateContextDisplay(toId) + addShadeWindow() } - - private fun overrideContextAndResources(newContext: Context) { - val contextWrapper = - shadeContext as? MutableContextWrapper - ?: error("Shade context is not a MutableContextWrapper!") - contextWrapper.baseContext = newContext - // Override needed in case someone is keeping a reference to the resources from the old - // context. - // TODO: b/362719719 - This shouldn't be needed, as resources should be updated when the - // window is moved to the new display automatically. - shadeResources.impl = shadeContext.resources.impl - } - - private fun dispatchConfigurationChanged(newConfig: Configuration) { - shadeConfigurationForwarder.onConfigurationChanged(newConfig) - shadeRootView.dispatchConfigurationChanged(newConfig) - shadeRootView.requestLayout() } - private fun registerConfigurationChange(context: Context) { - // we should keep only one at the time. - unregisterConfigChangedCallbacks?.invoke() - val callback = - object : ComponentCallbacks { - override fun onConfigurationChanged(newConfig: Configuration) { - dispatchConfigurationChanged(newConfig) - } + @UiThread + private fun removeShadeWindow(): Unit = + traceSection("removeShadeWindow") { wm.removeView(shadeRootView) } - override fun onLowMemory() {} - } - context.registerComponentCallbacks(callback) - unregisterConfigChangedCallbacks = { - context.unregisterComponentCallbacks(callback) - unregisterConfigChangedCallbacks = null - } - } + @UiThread + private fun addShadeWindow(): Unit = + traceSection("addShadeWindow") { wm.addView(shadeRootView, shadeLayoutParams) } - private fun getDisplayWindowProperties(displayId: Int): DisplayWindowProperties { - return displayWindowPropertiesRepository.get(displayId, TYPE_NOTIFICATION_SHADE) + @UiThread + private fun updateContextDisplay(newDisplayId: Int) { + traceSection("updateContextDisplay") { shadeContext.updateDisplay(newDisplayId) } } private companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java index c997ac5ad9df..6fd2d3fafb88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java @@ -768,8 +768,6 @@ public final class KeyboardShortcutListSearch { Intent.CATEGORY_APP_EMAIL, Intent.CATEGORY_APP_CALENDAR, Intent.CATEGORY_APP_MAPS, - Intent.CATEGORY_APP_MUSIC, - Intent.CATEGORY_APP_MESSAGING, Intent.CATEGORY_APP_CALCULATOR, }; String[] shortcutLabels = { @@ -778,19 +776,15 @@ public final class KeyboardShortcutListSearch { mContext.getString(R.string.keyboard_shortcut_group_applications_email), mContext.getString(R.string.keyboard_shortcut_group_applications_calendar), mContext.getString(R.string.keyboard_shortcut_group_applications_maps), - mContext.getString(R.string.keyboard_shortcut_group_applications_music), - mContext.getString(R.string.keyboard_shortcut_group_applications_sms), mContext.getString(R.string.keyboard_shortcut_group_applications_calculator) }; int[] keyCodes = { KeyEvent.KEYCODE_B, - KeyEvent.KEYCODE_C, + KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_E, - KeyEvent.KEYCODE_K, + KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_M, - KeyEvent.KEYCODE_P, - KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_U, }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index e19fcd05e9a4..85b8bf9aec80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -39,24 +39,24 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.HeadsUpManager import java.io.PrintWriter import javax.inject.Inject import kotlin.math.max /** * A utility class that handles notification panel expansion when a user swipes downward on a - * notification from the pulsing state. - * If face-bypass is enabled, the user can swipe down anywhere on the screen (not just from a - * notification) to trigger the notification panel expansion. + * notification from the pulsing state. If face-bypass is enabled, the user can swipe down anywhere + * on the screen (not just from a notification) to trigger the notification panel expansion. */ @SysUISingleton -class PulseExpansionHandler @Inject +class PulseExpansionHandler +@Inject constructor( context: Context, private val wakeUpCoordinator: NotificationWakeUpCoordinator, @@ -67,7 +67,7 @@ constructor( private val falsingManager: FalsingManager, private val shadeInteractor: ShadeInteractor, private val lockscreenShadeTransitionController: LockscreenShadeTransitionController, - dumpManager: DumpManager + dumpManager: DumpManager, ) : Gefingerpoken, Dumpable { companion object { private val SPRING_BACK_ANIMATION_LENGTH_MS = 375 @@ -91,14 +91,13 @@ constructor( pulseExpandAbortListener?.run() } } - headsUpManager.unpinAll( - /*userUnPinned= */ - true, - ) + headsUpManager.unpinAll(/* userUnPinned= */ true) } } + var leavingLockscreen: Boolean = false private set + private var touchSlop = 0f private var minDragDistance = 0 private lateinit var stackScrollerController: NotificationStackScrollLayoutController @@ -111,24 +110,27 @@ constructor( private val isFalseTouch: Boolean get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) + var pulseExpandAbortListener: Runnable? = null var bouncerShowing: Boolean = false init { initResources(context) - configurationController.addCallback(object : ConfigurationController.ConfigurationListener { - override fun onConfigChanged(newConfig: Configuration?) { - initResources(context) + configurationController.addCallback( + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + initResources(context) + } } - }) + ) mPowerManager = context.getSystemService(PowerManager::class.java) dumpManager.registerDumpable(this) } private fun initResources(context: Context) { - minDragDistance = context.resources.getDimensionPixelSize( - R.dimen.keyguard_drag_down_min_distance) + minDragDistance = + context.resources.getDimensionPixelSize(R.dimen.keyguard_drag_down_min_distance) touchSlop = ViewConfiguration.get(context).scaledTouchSlop.toFloat() } @@ -137,7 +139,8 @@ constructor( } private fun canHandleMotionEvent(): Boolean { - return wakeUpCoordinator.canShowPulsingHuns && !shadeInteractor.isQsExpanded.value && + return wakeUpCoordinator.canShowPulsingHuns && + !shadeInteractor.isQsExpanded.value && !bouncerShowing } @@ -189,18 +192,20 @@ constructor( } override fun onTouchEvent(event: MotionEvent): Boolean { - val finishExpanding = (event.action == MotionEvent.ACTION_CANCEL || - event.action == MotionEvent.ACTION_UP) && isExpanding + val finishExpanding = + (event.action == MotionEvent.ACTION_CANCEL || event.action == MotionEvent.ACTION_UP) && + isExpanding - val isDraggingNotificationOrCanBypass = mStartingChild?.showingPulsing() == true || - bypassController.canBypass() + val isDraggingNotificationOrCanBypass = + mStartingChild?.showingPulsing() == true || bypassController.canBypass() if ((!canHandleMotionEvent() || !isDraggingNotificationOrCanBypass) && !finishExpanding) { // We allow cancellations/finishing to still go through here to clean up the state return false } - if (velocityTracker == null || !isExpanding || - event.actionMasked == MotionEvent.ACTION_DOWN) { + if ( + velocityTracker == null || !isExpanding || event.actionMasked == MotionEvent.ACTION_DOWN + ) { return startExpansion(event) } velocityTracker!!.addMovement(event) @@ -210,12 +215,11 @@ constructor( when (event.actionMasked) { MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance) MotionEvent.ACTION_UP -> { - velocityTracker!!.computeCurrentVelocity( - /* units= */ - 1000, - ) - val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 && - statusBarStateController.state != StatusBarState.SHADE + velocityTracker!!.computeCurrentVelocity(/* units= */ 1000) + val canExpand = + moveDistance > 0 && + velocityTracker!!.getYVelocity() > -1000 && + statusBarStateController.state != StatusBarState.SHADE if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) { finishExpansion() } else { @@ -243,22 +247,16 @@ constructor( mPowerManager!!.wakeUp( SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, - "com.android.systemui:PULSEDRAG" + "com.android.systemui:PULSEDRAG", ) } - lockscreenShadeTransitionController.goToLockedShade( - startingChild, - needsQSAnimation = false - ) + lockscreenShadeTransitionController.goToLockedShade(startingChild, needsQSAnimation = false) lockscreenShadeTransitionController.finishPulseAnimation(cancelled = false) leavingLockscreen = true isExpanding = false if (mStartingChild is ExpandableNotificationRow) { val row = mStartingChild as ExpandableNotificationRow? - row!!.onExpandedByGesture( - /*userExpanded= */ - true, - ) + row!!.onExpandedByGesture(/* userExpanded= */ true) } } @@ -266,19 +264,15 @@ constructor( var expansionHeight = max(height, 0.0f) if (mStartingChild != null) { val child = mStartingChild!! - val newHeight = Math.min( - (child.collapsedHeight + expansionHeight).toInt(), - child.maxContentHeight - ) + val newHeight = + Math.min((child.collapsedHeight + expansionHeight).toInt(), child.maxContentHeight) child.actualHeight = newHeight } else { wakeUpCoordinator.setNotificationsVisibleForExpansion( - height - > lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications, - /*animate= */ - true, - /*increaseSpeed= */ - true + height > + lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications, + /*animate= */ true, + /*increaseSpeed= */ true, ) } lockscreenShadeTransitionController.setPulseHeight(expansionHeight, animate = false) @@ -296,7 +290,7 @@ constructor( @VisibleForTesting fun reset( child: ExpandableView, - animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() + animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong(), ) { if (child.actualHeight == child.collapsedHeight) { setUserLocked(child, false) @@ -309,11 +303,13 @@ constructor( // don't use reflection, because the `actualHeight` field may be obfuscated child.actualHeight = animation.animatedValue as Int } - anim.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - setUserLocked(child, false) + anim.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + setUserLocked(child, false) + } } - }) + ) anim.start() } @@ -331,12 +327,9 @@ constructor( } lockscreenShadeTransitionController.finishPulseAnimation(cancelled = true) wakeUpCoordinator.setNotificationsVisibleForExpansion( - /*visible= */ - false, - /*animate= */ - true, - /*increaseSpeed= */ - false + /*visible= */ false, + /*animate= */ true, + /*increaseSpeed= */ false, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt index 8ce0dbf8e171..6db610bbc3a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt @@ -21,7 +21,10 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import dagger.Binds +import dagger.Lazy import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey @@ -41,5 +44,19 @@ abstract class StatusBarChipsModule { fun provideChipsLogBuffer(factory: LogBufferFactory): LogBuffer { return factory.create("StatusBarChips", 200) } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(StatusBarNotificationChipsInteractor::class) + fun statusBarNotificationChipsInteractorAsCoreStartable( + interactorLazy: Lazy<StatusBarNotificationChipsInteractor> + ): CoreStartable { + return if (StatusBarNotifChips.isEnabled) { + interactorLazy.get() + } else { + CoreStartable.NOP + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt new file mode 100644 index 000000000000..087b51032fcf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.domain.interactor + +import com.android.systemui.activity.data.repository.ActivityManagerRepository +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger +import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad +import com.android.systemui.statusbar.chips.StatusBarChipsLog +import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map + +/** + * Interactor representing a single notification's status bar chip. + * + * [startingModel.key] dictates which notification this interactor corresponds to - all updates sent + * to this interactor via [setNotification] should only be for the notification with the same key. + * + * [StatusBarNotificationChipsInteractor] will collect all the individual instances of this + * interactor and send all the necessary information to the UI layer. + */ +@OptIn(ExperimentalCoroutinesApi::class) +class SingleNotificationChipInteractor +@AssistedInject +constructor( + @Assisted startingModel: ActiveNotificationModel, + private val activityManagerRepository: ActivityManagerRepository, + @StatusBarChipsLog private val logBuffer: LogBuffer, +) { + private val key = startingModel.key + private val logger = Logger(logBuffer, "Notif".pad()) + // [StatusBarChipLogTag] recommends a max tag length of 20, so [extraLogTag] should NOT be the + // top-level tag. It should instead be provided as the first string in each log message. + private val extraLogTag = "SingleChipInteractor[key=$key]" + + private val _notificationModel = MutableStateFlow(startingModel) + + /** + * Sets the new notification info corresponding to this interactor. The key on [model] *must* + * match the key on the original [startingModel], otherwise the update won't be processed. + */ + fun setNotification(model: ActiveNotificationModel) { + if (model.key != this.key) { + logger.w({ "$str1: received model for different key $str2" }) { + str1 = extraLogTag + str2 = model.key + } + return + } + _notificationModel.value = model + } + + private val uid: Flow<Int> = _notificationModel.map { it.uid } + + /** True if the application managing the notification is visible to the user. */ + private val isAppVisible: Flow<Boolean> = + uid.flatMapLatest { currentUid -> + activityManagerRepository.createIsAppVisibleFlow(currentUid, logger, extraLogTag) + } + + /** + * Emits this notification's status bar chip, or null if this notification shouldn't show a + * status bar chip. + */ + val notificationChip: Flow<NotificationChipModel?> = + combine(_notificationModel, isAppVisible) { notif, isAppVisible -> + if (isAppVisible) { + // If the app that posted this notification is visible, we want to hide the chip + // because information between the status bar chip and the app itself could be + // out-of-sync (like a timer that's slightly off) + null + } else { + notif.toNotificationChipModel() + } + } + + private fun ActiveNotificationModel.toNotificationChipModel(): NotificationChipModel? { + val statusBarChipIconView = this.statusBarChipIconView + if (statusBarChipIconView == null) { + logger.w({ "$str1: Can't show chip because status bar chip icon view is null" }) { + str1 = extraLogTag + } + return null + } + return NotificationChipModel(key, statusBarChipIconView) + } + + @AssistedFactory + fun interface Factory { + fun create(startingModel: ActiveNotificationModel): SingleNotificationChipInteractor + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt index 9e09671bc7bf..e8cb35b06999 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt @@ -17,16 +17,42 @@ package com.android.systemui.statusbar.chips.notification.domain.interactor import android.annotation.SuppressLint +import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger +import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad +import com.android.systemui.statusbar.chips.StatusBarChipsLog +import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf /** An interactor for the notification chips shown in the status bar. */ @SysUISingleton -class StatusBarNotificationChipsInteractor @Inject constructor() { +@OptIn(ExperimentalCoroutinesApi::class) +class StatusBarNotificationChipsInteractor +@Inject +constructor( + @Background private val backgroundScope: CoroutineScope, + private val activeNotificationsInteractor: ActiveNotificationsInteractor, + private val singleNotificationChipInteractorFactory: SingleNotificationChipInteractor.Factory, + @StatusBarChipsLog private val logBuffer: LogBuffer, +) : CoreStartable { + private val logger = Logger(logBuffer, "AllNotifs".pad()) // Each chip tap is an individual event, *not* a state, which is why we're using SharedFlow not // StateFlow. There shouldn't be multiple updates per frame, which should avoid performance @@ -45,4 +71,79 @@ class StatusBarNotificationChipsInteractor @Inject constructor() { StatusBarNotifChips.assertInNewMode() _promotedNotificationChipTapEvent.emit(key) } + + /** + * A cache of interactors. Each currently-promoted notification should have a corresponding + * interactor in this map. + */ + private val promotedNotificationInteractorMap = + mutableMapOf<String, SingleNotificationChipInteractor>() + + /** + * A list of interactors. Each currently-promoted notification should have a corresponding + * interactor in this list. + */ + private val promotedNotificationInteractors = + MutableStateFlow<List<SingleNotificationChipInteractor>>(emptyList()) + + override fun start() { + if (!StatusBarNotifChips.isEnabled) { + return + } + + backgroundScope.launch("StatusBarNotificationChipsInteractor") { + activeNotificationsInteractor.promotedOngoingNotifications + .pairwise(initialValue = emptyList()) + .collect { (oldNotifs, currentNotifs) -> + val removedNotifs = oldNotifs.minus(currentNotifs.toSet()) + removedNotifs.forEach { removedNotif -> + val wasRemoved = promotedNotificationInteractorMap.remove(removedNotif.key) + if (wasRemoved == null) { + logger.w({ + "Attempted to remove $str1 from interactor map but it wasn't present" + }) { + str1 = removedNotif.key + } + } + } + currentNotifs.forEach { notif -> + val interactor = + promotedNotificationInteractorMap.computeIfAbsent(notif.key) { + singleNotificationChipInteractorFactory.create(notif) + } + interactor.setNotification(notif) + } + logger.d({ "Interactors: $str1" }) { + str1 = + promotedNotificationInteractorMap.keys.joinToString(separator = " /// ") + } + promotedNotificationInteractors.value = + promotedNotificationInteractorMap.values.toList() + } + } + } + + /** + * A flow modeling the notifications that should be shown as chips in the status bar. Emits an + * empty list if there are no notifications that should show a status bar chip. + */ + val notificationChips: Flow<List<NotificationChipModel>> = + if (StatusBarNotifChips.isEnabled) { + // For all our current interactors... + promotedNotificationInteractors.flatMapLatest { interactors -> + if (interactors.isNotEmpty()) { + // Combine each interactor's [notificationChip] flow... + val allNotificationChips: List<Flow<NotificationChipModel?>> = + interactors.map { interactor -> interactor.notificationChip } + combine(allNotificationChips) { + // ... and emit just the non-null chips + it.filterNotNull() + } + } else { + flowOf(emptyList()) + } + } + } else { + flowOf(emptyList()) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt new file mode 100644 index 000000000000..5698ee6d1917 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.domain.model + +import com.android.systemui.statusbar.StatusBarIconView + +/** Modeling all the data needed to render a status bar notification chip. */ +data class NotificationChipModel(val key: String, val statusBarChipIconView: StatusBarIconView) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt index 752674854e2d..9eff627c8714 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -20,11 +20,10 @@ import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor +import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel -import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor -import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -37,7 +36,6 @@ class NotifChipsViewModel @Inject constructor( @Application private val applicationScope: CoroutineScope, - activeNotificationsInteractor: ActiveNotificationsInteractor, private val notifChipsInteractor: StatusBarNotificationChipsInteractor, ) { /** @@ -45,19 +43,14 @@ constructor( * no notifications that should show a status bar chip. */ val chips: Flow<List<OngoingActivityChipModel.Shown>> = - activeNotificationsInteractor.promotedOngoingNotifications.map { notifications -> - notifications.mapNotNull { it.toChipModel() } + notifChipsInteractor.notificationChips.map { notifications -> + notifications.map { it.toActivityChipModel() } } - /** - * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the - * notification has invalid data such that it can't be displayed as a chip. - */ - private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? { + /** Converts the notification to the [OngoingActivityChipModel] object. */ + private fun NotificationChipModel.toActivityChipModel(): OngoingActivityChipModel.Shown { StatusBarNotifChips.assertInNewMode() - // TODO(b/364653005): Log error if there's no icon view. - val rawIcon = this.statusBarChipIconView ?: return null - val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon) + val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(this.statusBarChipIconView) // TODO(b/364653005): Use the notification color if applicable. val colors = ColorsModel.Themed val onClickListener = @@ -65,7 +58,9 @@ constructor( // The notification pipeline needs everything to run on the main thread, so keep // this event on the main thread. applicationScope.launch { - notifChipsInteractor.onPromotedNotificationChipTapped(this@toChipModel.key) + notifChipsInteractor.onPromotedNotificationChipTapped( + this@toActivityChipModel.key + ) } } return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt index 84c7ab200fad..9e9a38e87924 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt @@ -26,6 +26,7 @@ import com.android.systemui.display.data.repository.DisplayScopeRepository import com.android.systemui.statusbar.data.repository.LightBarControllerStore import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStore import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import com.android.systemui.statusbar.phone.AutoHideControllerStore import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore import com.android.systemui.util.kotlin.pairwiseBy @@ -50,6 +51,7 @@ constructor( private val initializerStore: StatusBarInitializerStore, private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val statusBarInitializerStore: StatusBarInitializerStore, + private val autoHideControllerStore: AutoHideControllerStore, private val privacyDotWindowControllerStore: PrivacyDotWindowControllerStore, private val lightBarControllerStore: LightBarControllerStore, ) : CoreStartable { @@ -95,6 +97,7 @@ constructor( statusBarModeRepositoryStore.forDisplay(displayId), initializerStore.forDisplay(displayId), statusBarWindowControllerStore.forDisplay(displayId), + autoHideControllerStore.forDisplay(displayId), ) .start() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt index ff4760fd2837..9f5a3116948f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt @@ -71,9 +71,9 @@ constructor( @Assisted private val statusBarInitializer: StatusBarInitializer, @Assisted private val statusBarWindowController: StatusBarWindowController, @Main private val mainContext: CoroutineContext, + @Assisted private val autoHideController: AutoHideController, private val demoModeController: DemoModeController, private val pluginDependencyProvider: PluginDependencyProvider, - private val autoHideController: AutoHideController, private val remoteInputManager: NotificationRemoteInputManager, private val notificationShadeWindowViewControllerLazy: Lazy<NotificationShadeWindowViewController>, @@ -210,10 +210,6 @@ constructor( } private fun setUpAutoHide() { - if (displayId != Display.DEFAULT_DISPLAY) { - return - } - // TODO(b/373309973): per display implementation of auto hide controller autoHideController.setStatusBar( object : AutoHideUiElement { override fun synchronizeState() {} @@ -241,10 +237,7 @@ constructor( if (!demoModeController.isInDemoMode) { barTransitions.transitionTo(barMode.toTransitionModeInt(), animate) } - if (displayId == Display.DEFAULT_DISPLAY) { - // TODO(b/373309973): per display implementation of auto hide controller - autoHideController.touchAutoHide() - } + autoHideController.touchAutoHide() } private fun updateBubblesVisibility(statusBarVisible: Boolean) { @@ -288,6 +281,7 @@ constructor( statusBarModeRepository: StatusBarModePerDisplayRepository, statusBarInitializer: StatusBarInitializer, statusBarWindowController: StatusBarWindowController, + autoHideController: AutoHideController, ): StatusBarOrchestrator } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index 9b24d451f9cb..47b695e50a0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.data.repository.LightBarControllerStore import com.android.systemui.statusbar.phone.AutoHideController import com.android.systemui.statusbar.phone.AutoHideControllerImpl import com.android.systemui.statusbar.phone.LightBarController +import com.android.systemui.statusbar.phone.LightBarControllerImpl import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl import com.android.systemui.statusbar.phone.StatusBarSignalPolicy @@ -83,6 +84,11 @@ interface StatusBarModule { @Binds @SysUISingleton fun autoHideController(impl: AutoHideControllerImpl): AutoHideController + @Binds + fun lightBarControllerFactory( + legacyFactory: LightBarControllerImpl.LegacyFactory + ): LightBarController.Factory + companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 9a779300de97..3825c098ca5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -34,12 +34,12 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.row.NotificationRowContentBinderLogger import com.android.systemui.statusbar.notification.stack.StackStateAnimator -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.children import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt index 67c53d46b4d0..383227d2b3aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt @@ -22,10 +22,10 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.TransitionAnimator import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.HeadsUpUtil import kotlin.math.ceil import kotlin.math.max @@ -36,12 +36,12 @@ class NotificationLaunchAnimatorControllerProvider( private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor, private val notificationListContainer: NotificationListContainer, private val headsUpManager: HeadsUpManager, - private val jankMonitor: InteractionJankMonitor + private val jankMonitor: InteractionJankMonitor, ) { @JvmOverloads fun getAnimatorController( notification: ExpandableNotificationRow, - onFinishAnimationCallback: Runnable? = null + onFinishAnimationCallback: Runnable? = null, ): NotificationTransitionAnimatorController { return NotificationTransitionAnimatorController( notificationLaunchAnimationInteractor, @@ -49,7 +49,7 @@ class NotificationLaunchAnimatorControllerProvider( headsUpManager, notification, jankMonitor, - onFinishAnimationCallback + onFinishAnimationCallback, ) } } @@ -65,7 +65,7 @@ class NotificationTransitionAnimatorController( private val headsUpManager: HeadsUpManager, private val notification: ExpandableNotificationRow, private val jankMonitor: InteractionJankMonitor, - private val onFinishAnimationCallback: Runnable? + private val onFinishAnimationCallback: Runnable?, ) : ActivityTransitionAnimator.Controller { companion object { @@ -109,7 +109,7 @@ class NotificationTransitionAnimatorController( left = location[0], right = location[0] + notification.width, topCornerRadius = topCornerRadius, - bottomCornerRadius = notification.bottomCornerRadius + bottomCornerRadius = notification.bottomCornerRadius, ) params.startTranslationZ = notification.translationZ @@ -177,7 +177,7 @@ class NotificationTransitionAnimatorController( row.entry.key, true /* releaseImmediately */, animate, - reason + reason, ) } @@ -224,7 +224,7 @@ class NotificationTransitionAnimatorController( override fun onTransitionAnimationProgress( state: TransitionAnimator.State, progress: Float, - linearProgress: Float + linearProgress: Float, ) { val params = state as LaunchAnimationParameters params.progress = progress diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 08ffbf2b29d4..7a59f79c77a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -36,14 +36,14 @@ import com.android.systemui.shade.ShadeViewController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController.OnBypassStateChangedListener import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.doOnEnd import com.android.systemui.util.doOnStart import java.io.PrintWriter diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index c487ff5d35bd..6b84b6d07702 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -65,6 +65,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.icon.IconPack; import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -675,8 +676,8 @@ public final class NotificationEntry extends ListEntry { return row != null && row.isPinnedAndExpanded(); } - public void setRowPinned(boolean pinned) { - if (row != null) row.setPinned(pinned); + public void setRowPinnedStatus(PinnedStatus pinnedStatus) { + if (row != null) row.setPinnedStatus(pinnedStatus); } public boolean isRowHeadsUp() { @@ -1075,7 +1076,7 @@ public final class NotificationEntry extends ListEntry { if (PromotedNotificationContentModel.featureFlagEnabled()) { return mPromotedNotificationContentModel; } else { - Log.wtf(TAG, "getting promoted content without feature flag enabled"); + Log.wtf(TAG, "getting promoted content without feature flag enabled", new Throwable()); return null; } } @@ -1089,7 +1090,7 @@ public final class NotificationEntry extends ListEntry { if (PromotedNotificationContentModel.featureFlagEnabled()) { this.mPromotedNotificationContentModel = promotedNotificationContentModel; } else { - Log.wtf(TAG, "setting promoted content without feature flag enabled"); + Log.wtf(TAG, "setting promoted content without feature flag enabled", new Throwable()); } } 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 c7b47eeec218..0269b16d4490 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 @@ -41,13 +41,13 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.dagger.IncomingHeader +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.time.SystemClock import java.util.function.Consumer diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt index 366704e27b9b..de6f2576ff19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.annotation.SuppressLint import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager @@ -35,8 +36,8 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.headsUpEvents +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.headsUpEvents import com.android.systemui.util.asIndenting import com.android.systemui.util.indentIfPossible import java.io.PrintWriter @@ -52,7 +53,6 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.yield /** @@ -123,7 +123,7 @@ constructor( unseenNotifications.removeAll(notificationsSeenWhileLocked) logger.logAllMarkedSeenOnUnlock( seenCount = notificationsSeenWhileLocked.size, - remainingUnseenCount = unseenNotifications.size + remainingUnseenCount = unseenNotifications.size, ) notificationsSeenWhileLocked.clear() } @@ -140,7 +140,7 @@ constructor( * been "seen" while the device is on the keyguard. */ private suspend fun trackSeenNotificationsWhileLocked( - notificationsSeenWhileLocked: MutableSet<NotificationEntry>, + notificationsSeenWhileLocked: MutableSet<NotificationEntry> ) = coroutineScope { // Remove removed notifications from the set launch { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 8660cd117493..e75c11de57c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -45,7 +45,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; import com.android.systemui.statusbar.notification.shared.NotificationMinimalism; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.kotlin.BooleanFlowOperators; import com.android.systemui.util.kotlin.JavaAdapter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java index 5743ab0eae27..07fa6aeb7900 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java @@ -34,7 +34,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.Visual import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 42aadd132cd8..8a1371f1c415 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -77,6 +77,10 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.logging.dagger.NotificationsLogModule; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -88,7 +92,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModesCleanupStartable; import dagger.Binds; @@ -101,6 +105,8 @@ import kotlin.coroutines.CoroutineContext; import kotlinx.coroutines.CoroutineScope; +import java.util.Optional; + import javax.inject.Provider; /** @@ -308,4 +314,22 @@ public interface NotificationsModule { @IntoMap @ClassKey(ZenModesCleanupStartable.class) CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup); + + /** + * Provides {@link + * com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor} if + * one of the relevant feature flags is enabled. + */ + @Provides + @SysUISingleton + static Optional<PromotedNotificationContentExtractor> + providePromotedNotificationContentExtractor( + PromotedNotificationsProvider provider, Context context, + PromotedNotificationLogger logger) { + if (PromotedNotificationContentModel.featureFlagEnabled()) { + return Optional.of(new PromotedNotificationContentExtractor(provider, context, logger)); + } else { + return Optional.empty(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt index cf4fb25f638e..375983543c30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.notification.data import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository -import com.android.systemui.statusbar.policy.BaseHeadsUpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl import dagger.Binds import dagger.Module @Module(includes = [NotificationSettingsRepositoryModule::class]) interface NotificationDataLayerModule { - @Binds fun bindHeadsUpNotificationRepository(impl: BaseHeadsUpManager): HeadsUpRepository + @Binds fun bindHeadsUpNotificationRepository(impl: HeadsUpManagerImpl): HeadsUpRepository } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt index 7b40812d55c3..266310479a1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.data.repository +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey import kotlinx.coroutines.flow.StateFlow @@ -31,6 +32,6 @@ interface HeadsUpRowRepository : HeadsUpRowKey { /** A key to identify this row in the view hierarchy. */ val elementKey: Any - /** Whether this notification is "pinned", meaning that it should stay on top of the screen. */ - val isPinned: StateFlow<Boolean> + /** This notification's pinning status. */ + val pinnedStatus: StateFlow<PinnedStatus> } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt index e25127e3e0d6..64e78e4fbe48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt @@ -52,7 +52,9 @@ constructor( val topHeadsUpRowIfPinned: Flow<HeadsUpRowKey?> = headsUpRepository.topHeadsUpRow .flatMapLatest { repository -> - repository?.isPinned?.map { pinned -> repository.takeIf { pinned } } ?: flowOf(null) + repository?.pinnedStatus?.map { pinnedStatus -> + repository.takeIf { pinnedStatus.isPinned } + } ?: flowOf(null) } .distinctUntilChanged() @@ -64,7 +66,7 @@ constructor( if (repositories.isNotEmpty()) { val toCombine: List<Flow<Pair<HeadsUpRowRepository, Boolean>>> = repositories.map { repo -> - repo.isPinned.map { isPinned -> repo to isPinned } + repo.pinnedStatus.map { pinnedStatus -> repo to pinnedStatus.isPinned } } combine(toCombine) { pairs -> pairs.toSet() } } else { @@ -96,20 +98,17 @@ constructor( } /** Are there any pinned heads up rows to display? */ - val hasPinnedRows: Flow<Boolean> by lazy { - if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else { - headsUpRepository.activeHeadsUpRows.flatMapLatest { rows -> - if (rows.isNotEmpty()) { - combine(rows.map { it.isPinned }) { pins -> pins.any { it } } - } else { - // if the set is empty, there are no flows to combine - flowOf(false) + val hasPinnedRows: Flow<Boolean> = + headsUpRepository.activeHeadsUpRows.flatMapLatest { rows -> + if (rows.isNotEmpty()) { + combine(rows.map { it.pinnedStatus }) { pinnedStatus -> + pinnedStatus.any { it.isPinned } } + } else { + // if the set is empty, there are no flows to combine + flowOf(false) } } - } val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { @@ -139,15 +138,10 @@ constructor( } } - val showHeadsUpStatusBar: Flow<Boolean> by lazy { - if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else { - combine(hasPinnedRows, canShowHeadsUp) { hasPinnedRows, canShowHeadsUp -> - hasPinnedRows && canShowHeadsUp - } + val showHeadsUpStatusBar = + combine(hasPinnedRows, canShowHeadsUp) { hasPinnedRows, canShowHeadsUp -> + hasPinnedRows && canShowHeadsUp } - } fun headsUpRow(key: HeadsUpRowKey): HeadsUpRowInteractor = HeadsUpRowInteractor(key as HeadsUpRowRepository) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt index d406ed4e49c7..5c7c020a3bd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import android.os.Handler import android.util.Log @@ -24,29 +24,30 @@ import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun -import com.android.systemui.statusbar.policy.BaseHeadsUpManager.HeadsUpEntry import com.android.systemui.util.Compile import java.io.PrintWriter import javax.inject.Inject /* * Control when heads up notifications show during an avalanche where notifications arrive in fast - * succession, by delaying visual listener side effects and removal handling from BaseHeadsUpManager + * succession, by delaying visual listener side effects and removal handling from + * [HeadsUpManagerImpl]. */ @SysUISingleton class AvalancheController @Inject constructor( - dumpManager: DumpManager, - private val uiEventLogger: UiEventLogger, - private val headsUpManagerLogger: HeadsUpManagerLogger, - @Background private val bgHandler: Handler + dumpManager: DumpManager, + private val uiEventLogger: UiEventLogger, + private val headsUpManagerLogger: HeadsUpManagerLogger, + @Background private val bgHandler: Handler, ) : Dumpable { private val tag = "AvalancheController" private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG) - var baseEntryMapStr : () -> String = { "baseEntryMapStr not initialized" } + var baseEntryMapStr: () -> String = { "baseEntryMapStr not initialized" } var enableAtRuntime = true set(value) { @@ -119,23 +120,29 @@ constructor( if (runnable == null) { headsUpManagerLogger.logAvalancheUpdate( - caller, isEnabled, key, - "Runnable NULL, stop. ${getStateStr()}" + caller, + isEnabled, + key, + "Runnable NULL, stop. ${getStateStr()}", ) return } if (!isEnabled) { headsUpManagerLogger.logAvalancheUpdate( - caller, isEnabled, key, - "NOT ENABLED, run runnable. ${getStateStr()}" + caller, + isEnabled, + key, + "NOT ENABLED, run runnable. ${getStateStr()}", ) runnable.run() return } if (entry == null) { headsUpManagerLogger.logAvalancheUpdate( - caller, isEnabled, key, - "Entry NULL, stop. ${getStateStr()}" + caller, + isEnabled, + key, + "Entry NULL, stop. ${getStateStr()}", ) return } @@ -168,7 +175,7 @@ constructor( headsUpEntryShowing!!.updateEntry( /* updatePostTime= */ false, /* updateEarliestRemovalTime= */ false, - /* reason= */ "avalanche duration update" + /* reason= */ "avalanche duration update", ) } } @@ -192,24 +199,30 @@ constructor( if (runnable == null) { headsUpManagerLogger.logAvalancheDelete( - caller, isEnabled, key, - "Runnable NULL, stop. ${getStateStr()}" + caller, + isEnabled, + key, + "Runnable NULL, stop. ${getStateStr()}", ) return } if (!isEnabled) { runnable.run() headsUpManagerLogger.logAvalancheDelete( - caller, isEnabled = false, key, - "NOT ENABLED, run runnable. ${getStateStr()}" + caller, + isEnabled = false, + key, + "NOT ENABLED, run runnable. ${getStateStr()}", ) return } if (entry == null) { runnable.run() headsUpManagerLogger.logAvalancheDelete( - caller, isEnabled = true, key, - "Entry NULL, run runnable. ${getStateStr()}" + caller, + isEnabled = true, + key, + "Entry NULL, run runnable. ${getStateStr()}", ) return } @@ -219,11 +232,9 @@ constructor( if (entry in nextList) nextList.remove(entry) uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_REMOVED) outcome = "remove from next. ${getStateStr()}" - } else if (entry in debugDropSet) { debugDropSet.remove(entry) outcome = "remove from dropset. ${getStateStr()}" - } else if (isShowing(entry)) { previousHunKey = getKey(headsUpEntryShowing) // Show the next HUN before removing this one, so that we don't tell listeners @@ -233,7 +244,6 @@ constructor( showNext() runnable.run() outcome = "remove showing. ${getStateStr()}" - } else { runnable.run() outcome = "run runnable for untracked shown HUN. ${getStateStr()}" @@ -421,13 +431,13 @@ constructor( private fun getStateStr(): String { return "\navalanche state:" + - "\n\tshowing: [${getKey(headsUpEntryShowing)}]" + - "\n\tprevious: [$previousHunKey]" + - "\n\tnext list: $nextListStr" + - "\n\tnext map: $nextMapStr" + - "\n\tdropped: $dropSetStr" + - "\nBHUM.mHeadsUpEntryMap: " + - baseEntryMapStr() + "\n\tshowing: [${getKey(headsUpEntryShowing)}]" + + "\n\tprevious: [$previousHunKey]" + + "\n\tnext list: $nextListStr" + + "\n\tnext map: $nextMapStr" + + "\n\tdropped: $dropSetStr" + + "\nBHUM.mHeadsUpEntryMap: " + + baseEntryMapStr() } private val dropSetStr: String diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt index b37194b8b7a0..424a3c5e6af9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt @@ -1,4 +1,20 @@ -package com.android.systemui.statusbar.policy +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.headsup import android.graphics.Region import com.android.systemui.Dumpable diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt index 5e367509e774..6525b6f1186b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.statusbar.notification.collection.NotificationEntry diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java index 298ef7ee4bfa..99df9f45840a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.annotation.NonNull; import android.annotation.Nullable; @@ -55,6 +55,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.kotlin.JavaAdapter; @@ -84,7 +86,7 @@ import kotlinx.coroutines.flow.StateFlowKt; * they simply peek from the top of the screen. */ @SysUISingleton -public class BaseHeadsUpManager +public class HeadsUpManagerImpl implements HeadsUpManager, HeadsUpRepository, OnHeadsUpChangedListener { private static final String TAG = "BaseHeadsUpManager"; private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; @@ -180,7 +182,7 @@ public class BaseHeadsUpManager } @Inject - public BaseHeadsUpManager( + public HeadsUpManagerImpl( @NonNull final Context context, HeadsUpManagerLogger logger, StatusBarStateController statusBarStateController, @@ -403,9 +405,11 @@ public class BaseHeadsUpManager } if (shouldHeadsUpAgain) { headsUpEntry.updateEntry(true /* updatePostTime */, "updateNotification"); + PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(headsUpEntry.mEntry) + ? PinnedStatus.PinnedBySystem + : PinnedStatus.NotPinned; if (headsUpEntry != null) { - setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry), - "updateNotificationInternal"); + setEntryPinned(headsUpEntry, pinnedStatus, "updateNotificationInternal"); } } } @@ -417,7 +421,7 @@ public class BaseHeadsUpManager @Override public boolean shouldSwallowClick(@NonNull String key) { - BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); + HeadsUpManagerImpl.HeadsUpEntry entry = getHeadsUpEntry(key); return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime; } @@ -560,15 +564,16 @@ public class BaseHeadsUpManager } protected void setEntryPinned( - @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned, + @NonNull HeadsUpManagerImpl.HeadsUpEntry headsUpEntry, PinnedStatus pinnedStatus, String reason) { - mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned, reason); + mLogger.logSetEntryPinned(headsUpEntry.mEntry, pinnedStatus, reason); NotificationEntry entry = headsUpEntry.mEntry; + boolean isPinned = pinnedStatus.isPinned(); if (!isPinned) { headsUpEntry.mWasUnpinned = true; } - if (headsUpEntry.isRowPinned() != isPinned) { - headsUpEntry.setRowPinned(isPinned); + if (headsUpEntry.getPinnedStatus().getValue() != pinnedStatus) { + headsUpEntry.setRowPinnedStatus(pinnedStatus); updatePinnedMode(); if (isPinned && entry.getSbn() != null) { mUiEventLogger.logWithInstanceId( @@ -594,8 +599,10 @@ public class BaseHeadsUpManager NotificationEntry entry = headsUpEntry.mEntry; entry.setHeadsUp(true); - final boolean shouldPin = shouldHeadsUpBecomePinned(entry); - setEntryPinned(headsUpEntry, shouldPin, "onEntryAdded"); + final PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(entry) + ? PinnedStatus.PinnedBySystem + : PinnedStatus.NotPinned; + setEntryPinned(headsUpEntry, pinnedStatus, "onEntryAdded"); EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */); for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpStateChanged(entry, true); @@ -653,7 +660,7 @@ public class BaseHeadsUpManager protected void onEntryRemoved(HeadsUpEntry headsUpEntry, String reason) { NotificationEntry entry = headsUpEntry.mEntry; entry.setHeadsUp(false); - setEntryPinned(headsUpEntry, false /* isPinned */, "onEntryRemoved"); + setEntryPinned(headsUpEntry, PinnedStatus.NotPinned, "onEntryRemoved"); EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */); mLogger.logNotificationActuallyRemoved(entry); for (OnHeadsUpChangedListener listener : mListeners) { @@ -962,7 +969,7 @@ public class BaseHeadsUpManager Runnable runnable = () -> { mLogger.logUnpinEntry(key); - setEntryPinned(headsUpEntry, false /* isPinned */, "unpinAll"); + setEntryPinned(headsUpEntry, PinnedStatus.NotPinned, "unpinAll"); // maybe it got un sticky headsUpEntry.updateEntry(false /* updatePostTime */, "unpinAll"); @@ -1233,7 +1240,8 @@ public class BaseHeadsUpManager @Nullable private Runnable mCancelRemoveRunnable; private boolean mGutsShownPinned; - private final MutableStateFlow<Boolean> mIsPinned = StateFlowKt.MutableStateFlow(false); + private final MutableStateFlow<PinnedStatus> mPinnedStatus = + StateFlowKt.MutableStateFlow(PinnedStatus.NotPinned); /** * If the time this entry has been on was extended @@ -1269,8 +1277,8 @@ public class BaseHeadsUpManager @Override @NonNull - public StateFlow<Boolean> isPinned() { - return mIsPinned; + public StateFlow<PinnedStatus> getPinnedStatus() { + return mPinnedStatus; } /** Attach a NotificationEntry. */ @@ -1300,9 +1308,9 @@ public class BaseHeadsUpManager return mEntry != null && mEntry.isRowPinned(); } - protected void setRowPinned(boolean pinned) { - if (mEntry != null) mEntry.setRowPinned(pinned); - mIsPinned.setValue(pinned); + protected void setRowPinnedStatus(PinnedStatus pinnedStatus) { + if (mEntry != null) mEntry.setRowPinnedStatus(pinnedStatus); + mPinnedStatus.setValue(pinnedStatus); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt index 600270c7189a..80225c47e9ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel.INFO @@ -52,7 +52,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { caller: String, isEnabled: Boolean, notifEntryKey: String, - outcome: String + outcome: String, ) { buffer.log( TAG, @@ -63,7 +63,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str3 = outcome bool1 = isEnabled }, - { "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3" } + { "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3" }, ) } @@ -71,7 +71,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { caller: String, isEnabled: Boolean, notifEntryKey: String, - outcome: String + outcome: String, ) { buffer.log( TAG, @@ -82,7 +82,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str3 = outcome bool1 = isEnabled }, - { "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3" } + { "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3" }, ) } @@ -99,7 +99,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { long1 = delayMillis str2 = reason }, - { "schedule auto remove of $str1 in $long1 ms reason: $str2" } + { "schedule auto remove of $str1 in $long1 ms reason: $str2" }, ) } @@ -111,7 +111,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = entry.logKey str2 = reason }, - { "request: reschedule auto remove of $str1 reason: $str2" } + { "request: reschedule auto remove of $str1 reason: $str2" }, ) } @@ -124,7 +124,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { long1 = delayMillis str2 = reason }, - { "reschedule auto remove of $str1 in $long1 ms reason: $str2" } + { "reschedule auto remove of $str1 in $long1 ms reason: $str2" }, ) } @@ -136,7 +136,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = entry.logKey str2 = reason ?: "unknown" }, - { "request: cancel auto remove of $str1 reason: $str2" } + { "request: cancel auto remove of $str1 reason: $str2" }, ) } @@ -148,7 +148,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = entry.logKey str2 = reason ?: "unknown" }, - { "cancel auto remove of $str1 reason: $str2" } + { "cancel auto remove of $str1 reason: $str2" }, ) } @@ -161,7 +161,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str2 = reason bool1 = isWaiting }, - { "request: $str2 => remove entry $str1 isWaiting: $isWaiting" } + { "request: $str2 => remove entry $str1 isWaiting: $isWaiting" }, ) } @@ -174,7 +174,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str2 = reason bool1 = isWaiting }, - { "$str2 => remove entry $str1 isWaiting: $isWaiting" } + { "$str2 => remove entry $str1 isWaiting: $isWaiting" }, ) } @@ -190,7 +190,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { key: String, releaseImmediately: Boolean, isWaiting: Boolean, - reason: String + reason: String, ) { buffer.log( TAG, @@ -204,7 +204,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { { "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2 " + "reason: $str2" - } + }, ) } @@ -216,7 +216,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = logKey(key) str2 = reason }, - { "remove notification $str1 when headsUpEntry is null, reason: $str2" } + { "remove notification $str1 when headsUpEntry is null, reason: $str2" }, ) } @@ -233,7 +233,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { bool1 = alert bool2 = hasEntry }, - { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" } + { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" }, ) } @@ -246,7 +246,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { bool1 = alert bool2 = hasEntry }, - { "update notification $str1 alert: $bool1 hasEntry: $bool2" } + { "update notification $str1 alert: $bool1 hasEntry: $bool2" }, ) } @@ -259,7 +259,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { bool1 = updatePostTime str2 = reason ?: "unknown" }, - { "update entry $str1 updatePostTime: $bool1 reason: $str2" } + { "update entry $str1 updatePostTime: $bool1 reason: $str2" }, ) } @@ -268,20 +268,20 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { TAG, INFO, { int1 = packageSnoozeLengthMs }, - { "snooze length changed: ${int1}ms" } + { "snooze length changed: ${int1}ms" }, ) } - fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean, reason: String) { + fun logSetEntryPinned(entry: NotificationEntry, pinnedStatus: PinnedStatus, reason: String) { buffer.log( TAG, VERBOSE, { str1 = entry.logKey - bool1 = isPinned str2 = reason + str3 = pinnedStatus.name }, - { "$str2 => set entry pinned $str1 pinned: $bool1" } + { "$str2 => set entry pinned $str1 pinned: $str3" }, ) } @@ -290,16 +290,12 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { TAG, INFO, { bool1 = hasPinnedNotification }, - { "has pinned notification changed to $bool1" } + { "has pinned notification changed to $bool1" }, ) } fun logRemoveEntryAfterExpand(entry: NotificationEntry) { - buffer.log(TAG, VERBOSE, { - str1 = entry.logKey - }, { - "remove entry after expand: $str1" - }) + buffer.log(TAG, VERBOSE, { str1 = entry.logKey }, { "remove entry after expand: $str1" }) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpModule.kt index 83551e9b8294..f9502eeff270 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpModule.kt @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.policy.BaseHeadsUpManager -import com.android.systemui.statusbar.policy.HeadsUpManager import dagger.Binds import dagger.Module @Module interface HeadsUpModule { - @Binds @SysUISingleton fun bindsHeadsUpManager(hum: BaseHeadsUpManager): HeadsUpManager + @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerImpl): HeadsUpManager } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpNotificationViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpNotificationViewControllerEmptyImpl.kt index 021d30144b32..84754e530055 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpNotificationViewControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpNotificationViewControllerEmptyImpl.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNotificationViewController +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController /** Empty impl of [HeadsUpNotificationViewController] for use with Scene Container */ class HeadsUpNotificationViewControllerEmptyImpl : HeadsUpNotificationViewController { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpTouchHelper.java index a61463823613..3b6b9ed636fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpTouchHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.headsup; import android.content.Context; import android.os.RemoteException; @@ -27,7 +27,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; -import com.android.systemui.statusbar.policy.HeadsUpManager; /** * A helper class to handle touches on the heads-up views. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpUtil.java index f4a1975fab52..40da232ae598 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/OnHeadsUpChangedListener.java index de3bf0462d5b..b1fd78402570 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/OnHeadsUpChangedListener.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.annotation.NonNull; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/PinnedStatus.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/PinnedStatus.kt new file mode 100644 index 000000000000..af1805476f05 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/PinnedStatus.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.headsup + +/** + * A status representing whether and how a notification is pinned. + * + * @property isPinned true if a notification should be "pinned", meaning that a notification should + * stay on top of the screen. + */ +enum class PinnedStatus(val isPinned: Boolean) { + /** This notification is not pinned. */ + NotPinned(isPinned = false), + /** + * This notification is pinned by the system - likely because when the notification was added or + * updated, it required pinning. + */ + PinnedBySystem(isPinned = true), + /** + * This notification is pinned because the user did an explicit action to pin it (like tapping + * the notification chip in the status bar). + */ + // TODO(b/364653005): Use this status when a user taps the notification chip. + PinnedByUser(isPinned = true), +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/StatusBarHeadsUpChangeListener.java index 7145ffe1f515..23eb50700060 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/StatusBarHeadsUpChangeListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.statusbar.notification.headsup; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; @@ -26,14 +26,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import javax.inject.Inject; /** - * Ties the status bar to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. + * Ties the status bar to {@link HeadsUpManager}. */ @SysUISingleton public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, CoreStartable { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt index ec0827b4c478..caa6ccf9a5fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -52,13 +52,13 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE import com.android.systemui.statusbar.policy.BatteryController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.NotificationChannels import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings @@ -102,7 +102,7 @@ class PeekDisabledSuppressor( globalSettings.registerContentObserverSync( globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED), /* notifyForDescendants = */ true, - observer + observer, ) // QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused. @@ -139,7 +139,7 @@ class PeekPackageSnoozedSuppressor(private val headsUpManager: HeadsUpManager) : class PeekAlreadyBubbledSuppressor( private val statusBarStateController: StatusBarStateController, - private val bubbles: Optional<Bubbles> + private val bubbles: Optional<Bubbles>, ) : VisualInterruptionFilter(types = setOf(PEEK), reason = "already bubbled") { override fun shouldSuppress(entry: NotificationEntry) = when { @@ -164,7 +164,7 @@ class PeekNotImportantSuppressor : class PeekDeviceNotInUseSuppressor( private val powerManager: PowerManager, - private val statusBarStateController: StatusBarStateController + private val statusBarStateController: StatusBarStateController, ) : VisualInterruptionCondition(types = setOf(PEEK), reason = "device not in use") { override fun shouldSuppress() = when { @@ -177,7 +177,7 @@ class PeekOldWhenSuppressor(private val systemClock: SystemClock) : VisualInterruptionFilter( types = setOf(PEEK), reason = "has old `when`", - uiEventId = HUN_SUPPRESSED_OLD_WHEN + uiEventId = HUN_SUPPRESSED_OLD_WHEN, ) { private fun whenAge(entry: NotificationEntry) = systemClock.currentTimeMillis() - entry.sbn.notification.getWhen() @@ -206,7 +206,7 @@ class PulseEffectSuppressor : class PulseLockscreenVisibilityPrivateSuppressor : VisualInterruptionFilter( types = setOf(PULSE), - reason = "hidden by lockscreen visibility override" + reason = "hidden by lockscreen visibility override", ) { override fun shouldSuppress(entry: NotificationEntry) = entry.ranking.lockscreenVisibilityOverride == VISIBILITY_PRIVATE @@ -220,7 +220,7 @@ class PulseLowImportanceSuppressor : class HunGroupAlertBehaviorSuppressor : VisualInterruptionFilter( types = setOf(PEEK, PULSE), - reason = "suppressive group alert behavior" + reason = "suppressive group alert behavior", ) { override fun shouldSuppress(entry: NotificationEntry) = entry.sbn.let { it.isGroup && it.notification.suppressAlertingDueToGrouping() } @@ -282,11 +282,7 @@ class AvalancheSuppressor( private val notificationManager: NotificationManager, private val logger: VisualInterruptionDecisionLogger, private val systemSettings: SystemSettings, -) : - VisualInterruptionFilter( - types = setOf(PEEK, PULSE), - reason = "avalanche", - ) { +) : VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "avalanche") { val TAG = "AvalancheSuppressor" private val prefs = context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE) @@ -324,7 +320,7 @@ class AvalancheSuppressor( ALLOW_FSI_WITH_PERMISSION_ON, ALLOW_COLORIZED, ALLOW_EMERGENCY, - SUPPRESS + SUPPRESS, } enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum { @@ -354,6 +350,7 @@ class AvalancheSuppressor( AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY(1868), @UiEvent(doc = "HUN allowed during avalanche because it is a car warning") AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_WARNING(1869); + override fun getId(): Int { return id } @@ -399,7 +396,7 @@ class AvalancheSuppressor( val bundle = Bundle() bundle.putString( Notification.EXTRA_SUBSTITUTE_APP_NAME, - context.getString(com.android.internal.R.string.android_system_label) + context.getString(com.android.internal.R.string.android_system_label), ) val builder = @@ -452,7 +449,8 @@ class AvalancheSuppressor( if (entry.sbn.notification.category == CATEGORY_CAR_EMERGENCY) { uiEventLogger.log( - AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY) + AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY + ) return State.ALLOW_CATEGORY_CAR_EMERGENCY } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 450067a969e2..f586051feb56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -47,7 +47,7 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EventLog; import com.android.systemui.util.settings.GlobalSettings; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt index 52336be742cd..b831b9457acd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt @@ -32,6 +32,7 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision import com.android.systemui.statusbar.notification.interruption.VisualInterruptionSuppressor.EventLogData @@ -41,7 +42,6 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt new file mode 100644 index 000000000000..f400d605cb43 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted + +import android.app.Notification +import android.app.Notification.BigPictureStyle +import android.app.Notification.BigTextStyle +import android.app.Notification.CallStyle +import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN +import android.app.Notification.EXTRA_SUB_TEXT +import android.app.Notification.EXTRA_TEXT +import android.app.Notification.EXTRA_TITLE +import android.app.Notification.ProgressStyle +import android.content.Context +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When +import javax.inject.Inject + +@SysUISingleton +class PromotedNotificationContentExtractor +@Inject +constructor( + private val promotedNotificationsProvider: PromotedNotificationsProvider, + private val context: Context, + private val logger: PromotedNotificationLogger, +) { + fun extractContent( + entry: NotificationEntry, + recoveredBuilder: Notification.Builder, + ): PromotedNotificationContentModel? { + if (!PromotedNotificationContentModel.featureFlagEnabled()) { + logger.logExtractionSkipped(entry, "feature flags disabled") + return null + } + + if (!promotedNotificationsProvider.shouldPromote(entry)) { + logger.logExtractionSkipped(entry, "shouldPromote returned false") + return null + } + + val notification = entry.sbn.notification + if (notification == null) { + logger.logExtractionFailed(entry, "entry.sbn.notification is null") + return null + } + + val contentBuilder = PromotedNotificationContentModel.Builder(entry.key) + + // TODO: Pitch a fit if style is unsupported or mandatory fields are missing once + // FLAG_PROMOTED_ONGOING is set reliably and we're not testing status bar chips. + + contentBuilder.skeletonSmallIcon = entry.icons.aodIcon?.sourceIcon + contentBuilder.appName = notification.loadHeaderAppName(context) + contentBuilder.subText = notification.subText() + contentBuilder.time = notification.extractWhen() + contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs + contentBuilder.profileBadgeResId = null // TODO + contentBuilder.title = notification.title() + contentBuilder.text = notification.text() + contentBuilder.skeletonLargeIcon = null // TODO + + recoveredBuilder.style?.extractContent(contentBuilder) + ?: run { contentBuilder.style = Style.Ineligible } + + return contentBuilder.build().also { logger.logExtractionSucceeded(entry, it) } + } +} + +private fun Notification.title(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE) + +private fun Notification.text(): CharSequence? = extras?.getCharSequence(EXTRA_TEXT) + +private fun Notification.subText(): String? = extras?.getString(EXTRA_SUB_TEXT) + +private fun Notification.chronometerCountDown(): Boolean = + extras?.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, /* defaultValue= */ false) ?: false + +private fun Notification.extractWhen(): When? { + val time = `when` + val showsTime = showsTime() + val showsChronometer = showsChronometer() + val countDown = chronometerCountDown() + + return when { + showsTime -> When(time, When.Mode.Absolute) + showsChronometer -> When(time, if (countDown) When.Mode.CountDown else When.Mode.CountUp) + else -> null + } +} + +private fun Notification.Style.extractContent( + contentBuilder: PromotedNotificationContentModel.Builder +) { + contentBuilder.style = + when (this) { + is BigPictureStyle -> { + extractContent(contentBuilder) + Style.BigPicture + } + + is BigTextStyle -> { + extractContent(contentBuilder) + Style.BigText + } + + is CallStyle -> { + extractContent(contentBuilder) + Style.Call + } + + is ProgressStyle -> { + extractContent(contentBuilder) + Style.Progress + } + + else -> Style.Ineligible + } +} + +private fun BigPictureStyle.extractContent( + contentBuilder: PromotedNotificationContentModel.Builder +) { + // TODO? +} + +private fun BigTextStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) { + // TODO? +} + +private fun CallStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) { + contentBuilder.personIcon = null // TODO + contentBuilder.personName = null // TODO + contentBuilder.verificationIcon = null // TODO + contentBuilder.verificationText = null // TODO +} + +private fun ProgressStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) { + // TODO: Create NotificationProgressModel.toSkeleton, or something similar. + contentBuilder.progress = createProgressModel(0xffffffff.toInt(), 0x00000000) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt new file mode 100644 index 000000000000..13ad1413e89d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel.ERROR +import com.android.systemui.log.core.LogLevel.INFO +import com.android.systemui.log.dagger.NotificationLog +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.logKey +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import javax.inject.Inject + +class PromotedNotificationLogger +@Inject +constructor(@NotificationLog private val buffer: LogBuffer) { + fun logExtractionSkipped(entry: NotificationEntry, reason: String) { + buffer.log( + EXTRACTION_TAG, + INFO, + { + str1 = entry.logKey + str2 = reason + }, + { "extraction skipped: $str2 for $str1" }, + ) + } + + fun logExtractionFailed(entry: NotificationEntry, reason: String) { + buffer.log( + EXTRACTION_TAG, + ERROR, + { + str1 = entry.logKey + str2 = reason + }, + { "extraction failed: $str2 for $str1" }, + ) + } + + fun logExtractionSucceeded( + entry: NotificationEntry, + content: PromotedNotificationContentModel, + ) { + buffer.log( + EXTRACTION_TAG, + INFO, + { + str1 = entry.logKey + str2 = content.toString() + }, + { "extraction succeeded: $str2 for $str1" }, + ) + } +} + +private const val EXTRACTION_TAG = "PromotedNotificationContentExtractor" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt index 691dc6f5ccac..947d9e3e9547 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.promoted import android.app.Notification.FLAG_PROMOTED_ONGOING import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import javax.inject.Inject /** A provider for making decisions on which notifications should be promoted. */ @@ -30,7 +31,7 @@ interface PromotedNotificationsProvider { @SysUISingleton open class PromotedNotificationsProviderImpl @Inject constructor() : PromotedNotificationsProvider { override fun shouldPromote(entry: NotificationEntry): Boolean { - if (!PromotedNotificationUi.isEnabled) { + if (!PromotedNotificationContentModel.featureFlagEnabled()) { return false } return (entry.sbn.notification.flags and FLAG_PROMOTED_ONGOING) != 0 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 0480212e4d59..b7ab996a608c 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 @@ -102,6 +102,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -120,7 +121,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.SwipeableView; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.RemoteInputView; import com.android.systemui.statusbar.policy.SmartReplyConstants; @@ -280,7 +281,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationMenuRowPlugin mMenuRow; private ViewStub mGutsStub; private boolean mIsSystemChildExpanded; - private boolean mIsPinned; + private PinnedStatus mPinnedStatus = PinnedStatus.NotPinned; private boolean mExpandAnimationRunning; private AboveShelfChangedListener mAboveShelfChangedListener; private HeadsUpManager mHeadsUpManager; @@ -1228,17 +1229,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this * the notification will be rendered on top of the screen. - * - * @param pinned whether it is pinned */ - public void setPinned(boolean pinned) { + public void setPinnedStatus(PinnedStatus pinnedStatus) { int intrinsicHeight = getIntrinsicHeight(); boolean wasAboveShelf = isAboveShelf(); - mIsPinned = pinned; + mPinnedStatus = pinnedStatus; if (intrinsicHeight != getIntrinsicHeight()) { notifyHeightChanged(/* needsAnimation= */ false); } - if (pinned) { + if (pinnedStatus.isPinned()) { setAnimationRunning(true); mExpandedWhenPinned = false; } else if (mExpandedWhenPinned) { @@ -1252,7 +1251,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public boolean isPinned() { - return mIsPinned; + return mPinnedStatus.isPinned(); } @Override @@ -3832,7 +3831,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public boolean isAboveShelf() { return (canShowHeadsUp() - && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf) + && (mPinnedStatus.isPinned() + || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf) || mExpandAnimationRunning || mChildIsExpanding)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index ffe1b6f88302..a150f7f1f54e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -59,7 +59,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.time.SystemClock; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java index 77f5717d0e91..11db2fc77170 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java @@ -52,7 +52,7 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 41abac1d47f7..6e05e8e8b80e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -54,6 +54,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; @@ -92,6 +94,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final SmartReplyStateInflater mSmartReplyStateInflater; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; private final HeadsUpStyleProvider mHeadsUpStyleProvider; + private final PromotedNotificationContentExtractor mPromotedNotificationContentExtractor; private final NotificationRowContentBinderLogger mLogger; @@ -105,6 +108,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider, HeadsUpStyleProvider headsUpStyleProvider, + PromotedNotificationContentExtractor promotedNotificationContentExtractor, NotificationRowContentBinderLogger logger) { NotificationRowContentBinderRefactor.assertInLegacyMode(); mRemoteViewCache = remoteViewCache; @@ -115,6 +119,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mSmartReplyStateInflater = smartRepliesInflater; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; mHeadsUpStyleProvider = headsUpStyleProvider; + mPromotedNotificationContentExtractor = promotedNotificationContentExtractor; mLogger = logger; } @@ -165,6 +170,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mSmartReplyStateInflater, mNotifLayoutInflaterFactoryProvider, mHeadsUpStyleProvider, + mPromotedNotificationContentExtractor, mLogger); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); @@ -913,6 +919,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); logger.logAsyncTaskProgress(entry, "finishing"); + + if (PromotedNotificationContentModel.featureFlagEnabled()) { + entry.setPromotedNotificationContentModel(result.mExtractedPromotedNotificationContent); + } + boolean setRepliesAndActions = true; if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { if (result.inflatedContentView != null) { @@ -1123,6 +1134,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final SmartReplyStateInflater mSmartRepliesInflater; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; private final HeadsUpStyleProvider mHeadsUpStyleProvider; + private final PromotedNotificationContentExtractor mPromotedNotificationContentExtractor; private final NotificationRowContentBinderLogger mLogger; private AsyncInflationTask( @@ -1142,6 +1154,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider, HeadsUpStyleProvider headsUpStyleProvider, + PromotedNotificationContentExtractor promotedNotificationContentExtractor, NotificationRowContentBinderLogger logger) { mEntry = entry; mRow = row; @@ -1160,6 +1173,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mIsMediaInQS = isMediaFlagEnabled; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; mHeadsUpStyleProvider = headsUpStyleProvider; + mPromotedNotificationContentExtractor = promotedNotificationContentExtractor; mLogger = logger; entry.setInflationTask(this); } @@ -1276,6 +1290,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder ); } + if (PromotedNotificationContentModel.featureFlagEnabled()) { + mLogger.logAsyncTaskProgress(mEntry, "extracting promoted notification content"); + result.mExtractedPromotedNotificationContent = mPromotedNotificationContentExtractor + .extractContent(mEntry, recoveredBuilder); + mLogger.logAsyncTaskProgress(mEntry, "extracted promoted notification content: " + + result.mExtractedPromotedNotificationContent); + } + mLogger.logAsyncTaskProgress(mEntry, "getting row image resolver (on wrong thread!)"); final NotificationInlineImageResolver imageResolver = mRow.getImageResolver(); @@ -1377,6 +1399,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder @VisibleForTesting static class InflationProgress { + PromotedNotificationContentModel mExtractedPromotedNotificationContent; + private RemoteViews newContentView; private RemoteViews newHeadsUpView; private RemoteViews newExpandedView; 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 5ff9bc61f3ce..ea508748fada 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 @@ -72,7 +72,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.wmshell.BubblesManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt index d0c033bb10b0..c7d80e9d03ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row import android.annotation.SuppressLint +import android.app.Flags import android.app.Notification import android.content.Context import android.content.ContextWrapper @@ -46,6 +47,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.notification.ConversationNotificationProcessor import com.android.systemui.statusbar.notification.InflationException import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP @@ -95,6 +98,7 @@ constructor( private val smartReplyStateInflater: SmartReplyStateInflater, private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, private val headsUpStyleProvider: HeadsUpStyleProvider, + private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor, private val logger: NotificationRowContentBinderLogger, ) : NotificationRowContentBinder { @@ -147,6 +151,7 @@ constructor( /* isMediaFlagEnabled = */ smartReplyStateInflater, notifLayoutInflaterFactoryProvider, headsUpStyleProvider, + promotedNotificationContentExtractor, logger, ) if (inflateSynchronously) { @@ -166,6 +171,7 @@ constructor( builder: Notification.Builder, packageContext: Context, smartRepliesInflater: SmartReplyStateInflater, + promotedNotificationContentExtractor: PromotedNotificationContentExtractor, ): InflationProgress { val systemUIContext = row.context val result = @@ -182,6 +188,7 @@ constructor( notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider, headsUpStyleProvider = headsUpStyleProvider, conversationProcessor = conversationProcessor, + promotedNotificationContentExtractor = promotedNotificationContentExtractor, logger = logger, ) inflateSmartReplyViews( @@ -372,6 +379,7 @@ constructor( private val smartRepliesInflater: SmartReplyStateInflater, private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, private val headsUpStyleProvider: HeadsUpStyleProvider, + private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor, private val logger: NotificationRowContentBinderLogger, ) : AsyncTask<Void, Void, Result<InflationProgress>>(), InflationCallback, InflationTask { private val context: Context @@ -442,6 +450,7 @@ constructor( notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider, headsUpStyleProvider = headsUpStyleProvider, conversationProcessor = conversationProcessor, + promotedNotificationContentExtractor = promotedNotificationContentExtractor, logger = logger, ) logger.logAsyncTaskProgress( @@ -582,6 +591,7 @@ constructor( @VisibleForTesting val packageContext: Context, val remoteViews: NewRemoteViews, val contentModel: NotificationContentModel, + val extractedPromotedNotificationContentModel: PromotedNotificationContentModel?, ) { var inflatedContentView: View? = null @@ -670,8 +680,23 @@ constructor( notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, headsUpStyleProvider: HeadsUpStyleProvider, conversationProcessor: ConversationNotificationProcessor, + promotedNotificationContentExtractor: PromotedNotificationContentExtractor, logger: NotificationRowContentBinderLogger, ): InflationProgress { + val promoted = + if (PromotedNotificationContentModel.featureFlagEnabled()) { + logger.logAsyncTaskProgress(entry, "extracting promoted notification content") + val extracted = + promotedNotificationContentExtractor.extractContent(entry, builder) + logger.logAsyncTaskProgress( + entry, + "extracted promoted notification content: {extracted}", + ) + extracted + } else { + null + } + // process conversations and extract the messaging style val messagingStyle = if (entry.ranking.isConversation) { @@ -734,6 +759,7 @@ constructor( packageContext = packageContext, remoteViews = remoteViews, contentModel = contentModel, + extractedPromotedNotificationContentModel = promoted, ) } @@ -1393,6 +1419,11 @@ constructor( logger.logAsyncTaskProgress(entry, "finishing") entry.setContentModel(result.contentModel) + if (PromotedNotificationContentModel.featureFlagEnabled()) { + entry.promotedNotificationContentModel = + result.extractedPromotedNotificationContentModel + } + result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) } setContentViewsFromRemoteViews( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 777142194221..ad3611796d62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -39,7 +39,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.policy.AvalancheController; +import com.android.systemui.statusbar.notification.headsup.AvalancheController; import java.io.PrintWriter; 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 bddf6dffe7c0..223475e6e3b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -99,7 +99,7 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.FakeShadowView; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -127,7 +127,7 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrol import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.util.Assert; 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 3d7501deafc7..e89645d7cb94 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 @@ -98,9 +98,9 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpNotificationViewControllerEmptyImpl; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNotificationViewController; +import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; @@ -138,8 +138,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java index ef145578bbac..b2ffa4aa8233 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java @@ -33,7 +33,7 @@ import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.row.ExpandableView; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; import java.io.PrintWriter; import java.lang.reflect.Field; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index a55a165d9b66..01efd5de93ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -43,6 +43,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel @@ -124,6 +125,8 @@ constructor( private val notificationStackAppearanceInteractor: NotificationStackAppearanceInteractor, private val alternateBouncerToGoneTransitionViewModel: AlternateBouncerToGoneTransitionViewModel, + private val alternateBouncerToPrimaryBouncerTransitionViewModel: + AlternateBouncerToPrimaryBouncerTransitionViewModel, private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel, private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel, @@ -560,6 +563,7 @@ constructor( lockscreenToGoneTransitionViewModel.notificationAlpha(viewState), lockscreenToOccludedTransitionViewModel.lockscreenAlpha, lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha, + alternateBouncerToPrimaryBouncerTransitionViewModel.lockscreenAlpha, occludedToAodTransitionViewModel.lockscreenAlpha, occludedToGoneTransitionViewModel.notificationAlpha(viewState), occludedToLockscreenTransitionViewModel.lockscreenAlpha, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index 5209d0f1551e..7f95fb072ede 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -66,7 +66,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; 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 db294934c9ed..c6af3280eef1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -221,7 +221,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; @@ -401,7 +401,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final KeyguardBypassController mKeyguardBypassController; private final KeyguardStateController mKeyguardStateController; private final HeadsUpManager mHeadsUpManager; - private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + private final ShadeTouchableRegionManager mShadeTouchableRegionManager; private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; private final BroadcastDispatcher mBroadcastDispatcher; @@ -681,7 +681,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { KeyguardIndicationController keyguardIndicationController, DemoModeController demoModeController, Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy, - StatusBarTouchableRegionManager statusBarTouchableRegionManager, + ShadeTouchableRegionManager shadeTouchableRegionManager, BrightnessSliderController.Factory brightnessSliderFactory, ScreenOffAnimationController screenOffAnimationController, WallpaperController wallpaperController, @@ -724,7 +724,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mHeadsUpManager = headsUpManager; mBackActionInteractor = backActionInteractor; mKeyguardIndicationController = keyguardIndicationController; - mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; + mShadeTouchableRegionManager = shadeTouchableRegionManager; mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; mBroadcastDispatcher = broadcastDispatcher; @@ -1232,7 +1232,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mStatusBarInitializer.initializeStatusBar(); } - mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView()); + mShadeTouchableRegionManager.setup(getNotificationShadeWindowView()); if (!StatusBarConnectedDisplays.isEnabled()) { createNavigationBar(result); @@ -1856,10 +1856,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { pw.println(" mHeadsUpManager: null"); } - if (mStatusBarTouchableRegionManager != null) { - mStatusBarTouchableRegionManager.dump(pw, args); + if (mShadeTouchableRegionManager != null) { + mShadeTouchableRegionManager.dump(pw, args); } else { - pw.println(" mStatusBarTouchableRegionManager: null"); + pw.println(" mShadeTouchableRegionManager: null"); } if (mLightBarController != null) { @@ -2566,7 +2566,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { dismissVolumeDialog(); mWakeUpCoordinator.setFullyAwake(false); mKeyguardBypassController.onStartedGoingToSleep(); - mStatusBarTouchableRegionManager.updateTouchableRegion(); + mShadeTouchableRegionManager.updateTouchableRegion(); // The unlocked screen off and fold to aod animations might use our LightRevealScrim - // we need to be expanded for it to be visible. @@ -2655,7 +2655,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // once we fully woke up. updateRevealEffect(true /* wakingUp */); updateNotificationPanelTouchState(); - mStatusBarTouchableRegionManager.updateTouchableRegion(); + mShadeTouchableRegionManager.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). diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index ec92990441ce..57e26d708f26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -54,8 +54,8 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.util.Assert; import com.android.systemui.util.CopyOnLoopListenerSet; import com.android.systemui.util.IListenerSet; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 8de03d83d7af..677ed9f1ccdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -37,19 +37,20 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.core.StatusBarRootModernization; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope; import com.android.systemui.statusbar.policy.Clock; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.ViewController; import java.util.ArrayList; @@ -249,10 +250,14 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar updateParentClipping(false /* shouldClip */); mView.setVisibility(View.VISIBLE); show(mView); - hide(mClockView, View.INVISIBLE); + if (!StatusBarRootModernization.isEnabled()) { + hide(mClockView, View.INVISIBLE); + } mOperatorNameViewOptional.ifPresent(view -> hide(view, View.INVISIBLE)); } else { - show(mClockView); + if (!StatusBarRootModernization.isEnabled()) { + show(mClockView); + } mOperatorNameViewOptional.ifPresent(this::show); hide(mView, View.GONE, () -> { updateParentClipping(true /* shouldClip */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt index a6374a66806b..cd9b9d244e0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone +import android.content.Context import android.view.WindowInsetsController import com.android.internal.colorextraction.ColorExtractor import com.android.internal.view.AppearanceRegion @@ -64,4 +65,8 @@ interface LightBarController : CoreStartable { scrimBehindAlpha: Float, scrimInFrontColor: ColorExtractor.GradientColors, ) + + fun interface Factory { + fun create(context: Context): LightBarController + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java index ccb9a119d92f..ea67f1cdb60a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java @@ -22,6 +22,7 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT; +import android.content.Context; import android.graphics.Rect; import android.util.Log; import android.view.Display; @@ -34,12 +35,15 @@ import androidx.annotation.Nullable; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.view.AppearanceRegion; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.data.model.StatusBarAppearance; +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository; +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.Compile; import com.android.systemui.util.kotlin.JavaAdapterKt; @@ -55,6 +59,8 @@ import kotlinx.coroutines.CoroutineScope; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; + /** * Controls how light status bar flag applies to the icons. */ @@ -67,6 +73,7 @@ public class LightBarControllerImpl implements private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f; + private final int mDisplayId; private final CoroutineScope mCoroutineScope; private final SysuiDarkIconDispatcher mStatusBarIconController; private final BatteryController mBatteryController; @@ -140,6 +147,7 @@ public class LightBarControllerImpl implements DumpManager dumpManager, @Main CoroutineContext mainContext, BiometricUnlockController biometricUnlockController) { + mDisplayId = displayId; mCoroutineScope = coroutineScope; mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher; mBatteryController = batteryController; @@ -155,7 +163,12 @@ public class LightBarControllerImpl implements @Override public void start() { - mDumpManager.registerCriticalDumpable(mDumpableName, this); + if (mDisplayId == Display.DEFAULT_DISPLAY) { + // Can only register on default display, because NavigationBar creates its own instance + // as well as PerDisplayStore. + // TODO: b/380394368 - make sure there is only one instance per display. + mDumpManager.registerCriticalDumpable(mDumpableName, this); + } mBatteryController.addCallback(this); mNavigationMode = mNavModeController.addListener(mNavigationModeListener); JavaAdapterKt.collectFlow( @@ -490,4 +503,40 @@ public class LightBarControllerImpl implements DarkIconDispatcher darkIconDispatcher, StatusBarModePerDisplayRepository statusBarModePerDisplayRepository); } + + public static class LegacyFactory implements LightBarController.Factory { + + private final Factory mFactory; + private final CoroutineScope mApplicationScope; + private final DarkIconDispatcherStore mDarkIconDispatcherStore; + private final StatusBarModeRepositoryStore mStatusBarModeRepositoryStore; + + @Inject + public LegacyFactory( + LightBarControllerImpl.Factory factory, + @Application CoroutineScope applicationScope, + DarkIconDispatcherStore darkIconDispatcherStore, + StatusBarModeRepositoryStore statusBarModeRepositoryStore) { + mFactory = factory; + mApplicationScope = applicationScope; + mDarkIconDispatcherStore = darkIconDispatcherStore; + mStatusBarModeRepositoryStore = statusBarModeRepositoryStore; + } + + @NonNull + @Override + public LightBarController create(@NonNull Context context) { + // TODO: b/380394368 - Make sure correct per display instances are used. + LightBarControllerImpl lightBarController = mFactory.create( + context.getDisplayId(), + mApplicationScope, + mDarkIconDispatcherStore.getDefaultDisplay(), + mStatusBarModeRepositoryStore.getDefaultDisplay() + ); + // Calling start() manually to keep the legacy behavior. Before, LightBarControllerImpl + // was doing work in the constructor, which moved to start(). + lightBarController.start(); + return lightBarController; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManager.java index d2c2003112f6..bea8397fed62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManager.java @@ -47,8 +47,8 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.util.kotlin.JavaAdapter; import java.io.PrintWriter; @@ -62,7 +62,7 @@ import javax.inject.Provider; * of HeadsUpNotifications. */ @SysUISingleton -public final class StatusBarTouchableRegionManager implements Dumpable { +public final class ShadeTouchableRegionManager implements Dumpable { private static final String TAG = "TouchableRegionManager"; private final Context mContext; @@ -90,7 +90,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable { private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener; @Inject - public StatusBarTouchableRegionManager( + public ShadeTouchableRegionManager( Context context, NotificationShadeWindowController notificationShadeWindowController, ConfigurationController configurationController, @@ -165,7 +165,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable { @Override public void dump(PrintWriter pw, String[] args) { - pw.println("StatusBarTouchableRegionManager state:"); + pw.println("ShadeTouchableRegionManager state:"); pw.print(" mTouchableRegion="); pw.println(mTouchableRegion); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index aef26dea3c0d..bd1360f6e939 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -818,7 +818,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)} */ public void showPrimaryBouncer(boolean scrimmed) { - hideAlternateBouncer(false); + hideAlternateBouncer( + /* updateScrim= */ false, + // When the scene framework is on, don't ever clear the pending dismiss action from + /* clearDismissAction= */ !SceneContainerFlag.isEnabled()); if (mKeyguardStateController.isShowing() && !isBouncerShowing()) { if (SceneContainerFlag.isEnabled()) { mSceneInteractorLazy.get().changeScene( @@ -1005,6 +1008,15 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void hideAlternateBouncer(boolean updateScrim) { + hideAlternateBouncer(updateScrim, /* clearDismissAction= */ true); + } + + @Override + public void hideAlternateBouncer(boolean updateScrim, boolean clearDismissAction) { + if (clearDismissAction) { + mKeyguardDismissActionInteractor.get().clearDismissAction(); + } + updateAlternateBouncerShowing(mAlternateBouncerInteractor.hide() && updateScrim); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index af98311c937f..e33baf7c33ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -79,8 +79,8 @@ import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyS import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.wmshell.BubblesManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 79ea59c27090..b3cc047251ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -69,7 +69,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.Set; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index ee8c1ae6c5f9..4f32aaa2654e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -35,8 +35,6 @@ import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerSt import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule -import com.android.systemui.statusbar.phone.AutoHideController -import com.android.systemui.statusbar.phone.AutoHideControllerImpl import com.android.systemui.statusbar.phone.AutoHideControllerStore import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks import com.android.systemui.statusbar.phone.MultiDisplayAutoHideControllerStore @@ -80,9 +78,6 @@ interface StatusBarPhoneModule { @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer - @Binds - fun autoHideControllerFactory(impl: AutoHideControllerImpl.Factory): AutoHideController.Factory - companion object { /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */ @Provides @@ -128,6 +123,7 @@ interface StatusBarPhoneModule { statusBarModeRepositoryStore: StatusBarModeRepositoryStore, initializerStore: StatusBarInitializerStore, statusBarWindowControllerStore: StatusBarWindowControllerStore, + autoHideControllerStore: AutoHideControllerStore, statusBarOrchestratorFactory: StatusBarOrchestrator.Factory, ): StatusBarOrchestrator { return statusBarOrchestratorFactory.create( @@ -137,6 +133,7 @@ interface StatusBarPhoneModule { statusBarModeRepositoryStore.defaultDisplay, initializerStore.defaultDisplay, statusBarWindowControllerStore.defaultDisplay, + autoHideControllerStore.defaultDisplay, ) } @@ -207,5 +204,19 @@ interface StatusBarPhoneModule { singleDisplayLazy.get() } } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(AutoHideControllerStore::class) + fun storeAsCoreStartable( + multiDisplayLazy: Lazy<MultiDisplayAutoHideControllerStore> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + CoreStartable.NOP + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index aac2cd1755d0..78926c78a368 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -25,6 +25,7 @@ import android.app.UidObserver import android.content.Context import android.view.View import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.CoreStartable import com.android.systemui.Dumpable @@ -58,7 +59,6 @@ import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import com.android.app.tracing.coroutines.launchTraced as launch /** A controller to handle the ongoing call chip in the collapsed status bar. */ @SysUISingleton @@ -122,9 +122,9 @@ constructor( entry.sbn.uid, entry.sbn.notification.extras.getInt( Notification.EXTRA_CALL_TYPE, - -1 + -1, ) == CALL_TYPE_ONGOING, - statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false + statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false, ) if (newOngoingCallInfo == callNotificationInfo) { return @@ -236,7 +236,7 @@ constructor( bool1 = Flags.statusBarCallChipNotificationIcon() bool2 = currentInfo.notificationIconView != null }, - { "Creating OngoingCallModel.InCall. notifIconFlag=$bool1 hasIcon=$bool2" } + { "Creating OngoingCallModel.InCall. notifIconFlag=$bool1 hasIcon=$bool2" }, ) val icon = if (Flags.statusBarCallChipNotificationIcon()) { @@ -288,7 +288,7 @@ constructor( str1 = notifModel.callType.name bool1 = notifModel.statusBarChipIconView != null }, - { "NotifInteractorCallModel: key=$str1 when=$long1 callType=$str2 hasIcon=$bool1" } + { "NotifInteractorCallModel: key=$str1 when=$long1 callType=$str2 hasIcon=$bool1" }, ) val newOngoingCallInfo = @@ -299,7 +299,7 @@ constructor( notifModel.contentIntent, notifModel.uid, isOngoing = true, - statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false + statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false, ) if (newOngoingCallInfo == callNotificationInfo) { return @@ -378,7 +378,7 @@ constructor( ActivityTransitionAnimator.Controller.fromView( backgroundView, InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, - ) + ), ) } } @@ -455,7 +455,7 @@ constructor( /** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */ val isOngoing: Boolean, /** True if the user has swiped away the status bar while in this phone call. */ - val statusBarSwipedAway: Boolean + val statusBarSwipedAway: Boolean, ) { /** * Returns true if the notification information has a valid call start time. See @@ -472,6 +472,9 @@ constructor( /** * Observer to tell us when the app that posted the ongoing call notification is visible so that * we don't show the call chip at the same time (since the timers could be out-of-sync). + * + * For a more recommended architecture implementation, see + * [com.android.systemui.activity.data.repository.ActivityManagerRepository]. */ inner class CallAppUidObserver : UidObserver() { /** True if the application managing the call is visible to the user. */ @@ -512,7 +515,7 @@ constructor( uidObserver, ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_UNKNOWN, - context.opPackageName + context.opPackageName, ) isRegistered = true } catch (se: SecurityException) { @@ -537,7 +540,7 @@ constructor( uid: Int, procState: Int, procStateSeq: Long, - capability: Int + capability: Int, ) { val currentCallAppUid = callAppUid ?: return if (uid != currentCallAppUid) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt index b2a0272c06d1..a0cb8298bdb2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt @@ -30,13 +30,13 @@ import kotlinx.coroutines.flow.map @SysUISingleton class CollapsedStatusBarInteractor @Inject -constructor(DisableFlagsInteractor: DisableFlagsInteractor) { +constructor(disableFlagsInteractor: DisableFlagsInteractor) { /** * The visibilities of various status bar child views, based only on the information we received * from disable flags. */ val visibilityViaDisableFlags: Flow<StatusBarDisableFlagsVisibilityModel> = - DisableFlagsInteractor.disableFlags.map { + disableFlagsInteractor.disableFlags.map { StatusBarDisableFlagsVisibilityModel( isClockAllowed = it.isClockEnabled, areNotificationIconsAllowed = it.areNotificationIconsEnabled, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt index 6a9b43c995e4..c52275a9eaa4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt @@ -32,6 +32,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel @@ -39,6 +40,7 @@ import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEvent import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.CollapsedStatusBarInteractor @@ -137,6 +139,7 @@ constructor( collapsedStatusBarInteractor: CollapsedStatusBarInteractor, private val lightsOutInteractor: LightsOutInteractor, private val notificationsInteractor: ActiveNotificationsInteractor, + headsUpNotificationInteractor: HeadsUpNotificationInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, keyguardInteractor: KeyguardInteractor, sceneInteractor: SceneInteractor, @@ -222,27 +225,43 @@ constructor( isHomeStatusBarAllowed && !isSecureCameraActive } + private val isAnyChipVisible = + if (StatusBarNotifChips.isEnabled) { + ongoingActivityChips.map { it.primary is OngoingActivityChipModel.Shown } + } else { + primaryOngoingActivityChip.map { it is OngoingActivityChipModel.Shown } + } + override val isClockVisible: Flow<VisibilityModel> = combine( shouldHomeStatusBarBeVisible, + headsUpNotificationInteractor.showHeadsUpStatusBar, collapsedStatusBarInteractor.visibilityViaDisableFlags, - ) { shouldStatusBarBeVisible, visibilityViaDisableFlags -> - val showClock = shouldStatusBarBeVisible && visibilityViaDisableFlags.isClockAllowed - // TODO(b/364360986): Take CollapsedStatusBarFragment.clockHiddenMode into account. - VisibilityModel(showClock.toVisibilityInt(), visibilityViaDisableFlags.animate) + ) { shouldStatusBarBeVisible, showHeadsUp, visibilityViaDisableFlags -> + val showClock = + shouldStatusBarBeVisible && visibilityViaDisableFlags.isClockAllowed && !showHeadsUp + // Always use View.INVISIBLE here, so that animations work + VisibilityModel(showClock.toVisibleOrInvisible(), visibilityViaDisableFlags.animate) } override val isNotificationIconContainerVisible: Flow<VisibilityModel> = combine( shouldHomeStatusBarBeVisible, + isAnyChipVisible, collapsedStatusBarInteractor.visibilityViaDisableFlags, - ) { shouldStatusBarBeVisible, visibilityViaDisableFlags -> + ) { shouldStatusBarBeVisible, anyChipVisible, visibilityViaDisableFlags -> val showNotificationIconContainer = - shouldStatusBarBeVisible && visibilityViaDisableFlags.areNotificationIconsAllowed + if (anyChipVisible) { + false + } else { + shouldStatusBarBeVisible && + visibilityViaDisableFlags.areNotificationIconsAllowed + } VisibilityModel( - showNotificationIconContainer.toVisibilityInt(), + showNotificationIconContainer.toVisibleOrGone(), visibilityViaDisableFlags.animate, ) } + private val isSystemInfoVisible = combine( shouldHomeStatusBarBeVisible, @@ -250,7 +269,7 @@ constructor( ) { shouldStatusBarBeVisible, visibilityViaDisableFlags -> val showSystemInfo = shouldStatusBarBeVisible && visibilityViaDisableFlags.isSystemInfoAllowed - VisibilityModel(showSystemInfo.toVisibilityInt(), visibilityViaDisableFlags.animate) + VisibilityModel(showSystemInfo.toVisibleOrGone(), visibilityViaDisableFlags.animate) } override val systemInfoCombinedVis = @@ -270,7 +289,11 @@ constructor( ) @View.Visibility - private fun Boolean.toVisibilityInt(): Int { + private fun Boolean.toVisibleOrGone(): Int { return if (this) View.VISIBLE else View.GONE } + + // Similar to the above, but uses INVISIBLE in place of GONE + @View.Visibility + private fun Boolean.toVisibleOrInvisible(): Int = if (this) View.VISIBLE else View.INVISIBLE } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt index 616992eb0865..56c9e9abbc36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt @@ -41,8 +41,8 @@ import android.view.ViewGroup import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction import android.widget.Button -import com.android.systemui.res.R import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.shared.system.DevicePolicyManagerWrapper import com.android.systemui.shared.system.PackageManagerWrapper @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.NotificationUiAdjustment import com.android.systemui.statusbar.SmartReplyController import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.logging.NotificationLogger import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions @@ -63,40 +64,42 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.system.measureTimeMillis - /** Returns whether we should show the smart reply view and its smart suggestions. */ fun shouldShowSmartReplyView( entry: NotificationEntry, - smartReplyState: InflatedSmartReplyState + smartReplyState: InflatedSmartReplyState, ): Boolean { - if (smartReplyState.smartReplies == null && - smartReplyState.smartActions == null) { + if (smartReplyState.smartReplies == null && smartReplyState.smartActions == null) { // There are no smart replies and no smart actions. return false } // If we are showing the spinner we don't want to add the buttons. - val showingSpinner = entry.sbn.notification.extras - .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false) + val showingSpinner = + entry.sbn.notification.extras.getBoolean( + Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, + false, + ) if (showingSpinner) { return false } // If we are keeping the notification around while sending we don't want to add the buttons. - return !entry.sbn.notification.extras - .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false) + return !entry.sbn.notification.extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false) } /** Determines if two [InflatedSmartReplyState] are visually similar. */ fun areSuggestionsSimilar( left: InflatedSmartReplyState?, - right: InflatedSmartReplyState? -): Boolean = when { - left === right -> true - left == null || right == null -> false - left.hasPhishingAction != right.hasPhishingAction -> false - left.smartRepliesList != right.smartRepliesList -> false - left.suppressedActionIndices != right.suppressedActionIndices -> false - else -> !NotificationUiAdjustment.areDifferent(left.smartActionsList, right.smartActionsList) -} + right: InflatedSmartReplyState?, +): Boolean = + when { + left === right -> true + left == null || right == null -> false + left.hasPhishingAction != right.hasPhishingAction -> false + left.smartRepliesList != right.smartRepliesList -> false + left.suppressedActionIndices != right.suppressedActionIndices -> false + else -> + !NotificationUiAdjustment.areDifferent(left.smartActionsList, right.smartActionsList) + } interface SmartReplyStateInflater { fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState @@ -106,181 +109,211 @@ interface SmartReplyStateInflater { notifPackageContext: Context, entry: NotificationEntry, existingSmartReplyState: InflatedSmartReplyState?, - newSmartReplyState: InflatedSmartReplyState + newSmartReplyState: InflatedSmartReplyState, ): InflatedSmartReplyViewHolder } -/*internal*/ class SmartReplyStateInflaterImpl @Inject constructor( +/*internal*/ class SmartReplyStateInflaterImpl +@Inject +constructor( private val constants: SmartReplyConstants, private val activityManagerWrapper: ActivityManagerWrapper, private val packageManagerWrapper: PackageManagerWrapper, private val devicePolicyManagerWrapper: DevicePolicyManagerWrapper, private val smartRepliesInflater: SmartReplyInflater, - private val smartActionsInflater: SmartActionInflater + private val smartActionsInflater: SmartActionInflater, ) : SmartReplyStateInflater { override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState = - chooseSmartRepliesAndActions(entry) + chooseSmartRepliesAndActions(entry) override fun inflateSmartReplyViewHolder( sysuiContext: Context, notifPackageContext: Context, entry: NotificationEntry, existingSmartReplyState: InflatedSmartReplyState?, - newSmartReplyState: InflatedSmartReplyState + newSmartReplyState: InflatedSmartReplyState, ): InflatedSmartReplyViewHolder { if (!shouldShowSmartReplyView(entry, newSmartReplyState)) { return InflatedSmartReplyViewHolder( - null /* smartReplyView */, - null /* smartSuggestionButtons */) + null /* smartReplyView */, + null, /* smartSuggestionButtons */ + ) } // Only block clicks if the smart buttons are different from the previous set - to avoid // scenarios where a user incorrectly cannot click smart buttons because the // notification is updated. val delayOnClickListener = - !areSuggestionsSimilar(existingSmartReplyState, newSmartReplyState) + !areSuggestionsSimilar(existingSmartReplyState, newSmartReplyState) val smartReplyView = SmartReplyView.inflate(sysuiContext, constants) val smartReplies = newSmartReplyState.smartReplies smartReplyView.setSmartRepliesGeneratedByAssistant(smartReplies?.fromAssistant ?: false) - val smartReplyButtons = smartReplies?.let { - smartReplies.choices.asSequence().mapIndexed { index, choice -> - smartRepliesInflater.inflateReplyButton( + val smartReplyButtons = + smartReplies?.let { + smartReplies.choices.asSequence().mapIndexed { index, choice -> + smartRepliesInflater.inflateReplyButton( smartReplyView, entry, smartReplies, index, choice, - delayOnClickListener) - } - } ?: emptySequence() + delayOnClickListener, + ) + } + } ?: emptySequence() - val smartActionButtons = newSmartReplyState.smartActions?.let { smartActions -> - val themedPackageContext = + val smartActionButtons = + newSmartReplyState.smartActions?.let { smartActions -> + val themedPackageContext = ContextThemeWrapper(notifPackageContext, sysuiContext.theme) - smartActions.actions.asSequence() + smartActions.actions + .asSequence() .filter { it.actionIntent != null } .mapIndexed { index, action -> smartActionsInflater.inflateActionButton( - smartReplyView, - entry, - smartActions, - index, - action, - delayOnClickListener, - themedPackageContext) + smartReplyView, + entry, + smartActions, + index, + action, + delayOnClickListener, + themedPackageContext, + ) } - } ?: emptySequence() + } ?: emptySequence() return InflatedSmartReplyViewHolder( - smartReplyView, - (smartReplyButtons + smartActionButtons).toList()) + smartReplyView, + (smartReplyButtons + smartActionButtons).toList(), + ) } /** * Chose what smart replies and smart actions to display. App generated suggestions take - * precedence. So if the app provides any smart replies, we don't show any - * replies or actions generated by the NotificationAssistantService (NAS), and if the app - * provides any smart actions we also don't show any NAS-generated replies or actions. + * precedence. So if the app provides any smart replies, we don't show any replies or actions + * generated by the NotificationAssistantService (NAS), and if the app provides any smart + * actions we also don't show any NAS-generated replies or actions. */ fun chooseSmartRepliesAndActions(entry: NotificationEntry): InflatedSmartReplyState { val notification = entry.sbn.notification val remoteInputActionPair = notification.findRemoteInputActionPair(false /* freeform */) val freeformRemoteInputActionPair = - notification.findRemoteInputActionPair(true /* freeform */) + notification.findRemoteInputActionPair(true /* freeform */) if (!constants.isEnabled) { if (DEBUG) { - Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " + - entry.sbn.key) + Log.d( + TAG, + "Smart suggestions not enabled, not adding suggestions for " + entry.sbn.key, + ) } return InflatedSmartReplyState(null, null, null, false) } // Only use smart replies from the app if they target P or above. We have this check because // the smart reply API has been used for other things (Wearables) in the past. The API to // add smart actions is new in Q so it doesn't require a target-sdk check. - val enableAppGeneratedSmartReplies = (!constants.requiresTargetingP() || - entry.targetSdk >= Build.VERSION_CODES.P) + val enableAppGeneratedSmartReplies = + (!constants.requiresTargetingP() || entry.targetSdk >= Build.VERSION_CODES.P) val appGeneratedSmartActions = notification.contextualActions - var smartReplies: SmartReplies? = when { - enableAppGeneratedSmartReplies -> remoteInputActionPair?.let { pair -> - pair.second.actionIntent?.let { actionIntent -> - if (pair.first.choices?.isNotEmpty() == true) - SmartReplies( - pair.first.choices.asList(), - pair.first, - actionIntent, - false /* fromAssistant */) - else null - } + var smartReplies: SmartReplies? = + when { + enableAppGeneratedSmartReplies -> + remoteInputActionPair?.let { pair -> + pair.second.actionIntent?.let { actionIntent -> + if (pair.first.choices?.isNotEmpty() == true) + SmartReplies( + pair.first.choices.asList(), + pair.first, + actionIntent, + false, /* fromAssistant */ + ) + else null + } + } + else -> null + } + var smartActions: SmartActions? = + when { + appGeneratedSmartActions.isNotEmpty() -> + SmartActions(appGeneratedSmartActions, false /* fromAssistant */) + else -> null } - else -> null - } - var smartActions: SmartActions? = when { - appGeneratedSmartActions.isNotEmpty() -> - SmartActions(appGeneratedSmartActions, false /* fromAssistant */) - else -> null - } // Apps didn't provide any smart replies / actions, use those from NAS (if any). if (smartReplies == null && smartActions == null) { val entryReplies = entry.smartReplies val entryActions = entry.smartActions - if (entryReplies.isNotEmpty() && + if ( + entryReplies.isNotEmpty() && freeformRemoteInputActionPair != null && freeformRemoteInputActionPair.second.allowGeneratedReplies && - freeformRemoteInputActionPair.second.actionIntent != null) { - smartReplies = SmartReplies( + freeformRemoteInputActionPair.second.actionIntent != null + ) { + smartReplies = + SmartReplies( entryReplies, freeformRemoteInputActionPair.first, freeformRemoteInputActionPair.second.actionIntent, - true /* fromAssistant */) + true, /* fromAssistant */ + ) } - if (entryActions.isNotEmpty() && - notification.allowSystemGeneratedContextualActions) { - val systemGeneratedActions: List<Notification.Action> = when { - activityManagerWrapper.isLockTaskKioskModeActive -> - // Filter actions if we're in kiosk-mode - we don't care about screen - // pinning mode, since notifications aren't shown there anyway. - filterAllowlistedLockTaskApps(entryActions) - else -> entryActions - } + if (entryActions.isNotEmpty() && notification.allowSystemGeneratedContextualActions) { + val systemGeneratedActions: List<Notification.Action> = + when { + activityManagerWrapper.isLockTaskKioskModeActive -> + // Filter actions if we're in kiosk-mode - we don't care about screen + // pinning mode, since notifications aren't shown there anyway. + filterAllowlistedLockTaskApps(entryActions) + else -> entryActions + } smartActions = SmartActions(systemGeneratedActions, true /* fromAssistant */) } } - val hasPhishingAction = smartActions?.actions?.any { - it.isContextual && it.semanticAction == - Notification.Action.SEMANTIC_ACTION_CONVERSATION_IS_PHISHING - } ?: false + val hasPhishingAction = + smartActions?.actions?.any { + it.isContextual && + it.semanticAction == + Notification.Action.SEMANTIC_ACTION_CONVERSATION_IS_PHISHING + } ?: false var suppressedActions: SuppressedActions? = null if (hasPhishingAction) { // If there is a phishing action, calculate the indices of the actions with RemoteInput // as those need to be hidden from the view. - val suppressedActionIndices = notification.actions.mapIndexedNotNull { index, action -> - if (action.remoteInputs?.isNotEmpty() == true) index else null - } + val suppressedActionIndices = + notification.actions.mapIndexedNotNull { index, action -> + if (action.remoteInputs?.isNotEmpty() == true) index else null + } suppressedActions = SuppressedActions(suppressedActionIndices) } - return InflatedSmartReplyState(smartReplies, smartActions, suppressedActions, - hasPhishingAction) + return InflatedSmartReplyState( + smartReplies, + smartActions, + suppressedActions, + hasPhishingAction, + ) } /** - * Filter actions so that only actions pointing to allowlisted apps are permitted. - * This filtering is only meaningful when in lock-task mode. + * Filter actions so that only actions pointing to allowlisted apps are permitted. This + * filtering is only meaningful when in lock-task mode. */ private fun filterAllowlistedLockTaskApps( actions: List<Notification.Action> - ): List<Notification.Action> = actions.filter { action -> - // Only allow actions that are explicit (implicit intents are not handled in lock-task - // mode), and link to allowlisted apps. - action.actionIntent?.intent?.let { intent -> - packageManagerWrapper.resolveActivity(intent, 0 /* flags */) - }?.let { resolveInfo -> - devicePolicyManagerWrapper.isLockTaskPermitted(resolveInfo.activityInfo.packageName) - } ?: false - } + ): List<Notification.Action> = + actions.filter { action -> + // Only allow actions that are explicit (implicit intents are not handled in lock-task + // mode), and link to allowlisted apps. + action.actionIntent + ?.intent + ?.let { intent -> packageManagerWrapper.resolveActivity(intent, 0 /* flags */) } + ?.let { resolveInfo -> + devicePolicyManagerWrapper.isLockTaskPermitted( + resolveInfo.activityInfo.packageName + ) + } ?: false + } } interface SmartActionInflater { @@ -291,7 +324,7 @@ interface SmartActionInflater { actionIndex: Int, action: Notification.Action, delayOnClickListener: Boolean, - packageContext: Context + packageContext: Context, ): Button } @@ -310,28 +343,32 @@ private fun loadIconDrawableWithTimeout( val bitmap: Bitmap? val durationMillis = measureTimeMillis { val source = ImageDecoder.createSource(packageContext.contentResolver, icon.uri) - bitmap = ImageDecoder.decodeBitmap(source) { decoder, _, _ -> - decoder.setTargetSize(targetSize, targetSize) - decoder.allocator = ImageDecoder.ALLOCATOR_DEFAULT - } + bitmap = + ImageDecoder.decodeBitmap(source) { decoder, _, _ -> + decoder.setTargetSize(targetSize, targetSize) + decoder.allocator = ImageDecoder.ALLOCATOR_DEFAULT + } } if (durationMillis > ICON_TASK_TIMEOUT_MS) { Log.w(TAG, "Loading $icon took ${durationMillis / 1000f} sec") } checkNotNull(bitmap) { "ImageDecoder.decodeBitmap() returned null" } } - val bitmap = runCatching { - iconTaskThreadPool.execute(bitmapTask) - bitmapTask.get(ICON_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS) - }.getOrElse { ex -> - Log.e(TAG, "Failed to load $icon: $ex") - bitmapTask.cancel(true) - return null - } + val bitmap = + runCatching { + iconTaskThreadPool.execute(bitmapTask) + bitmapTask.get(ICON_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS) + } + .getOrElse { ex -> + Log.e(TAG, "Failed to load $icon: $ex") + bitmapTask.cancel(true) + return null + } // TODO(b/288561520): rewrite Icon so that we don't need to duplicate this logic val bitmapDrawable = BitmapDrawable(packageContext.resources, bitmap) - val result = if (icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) - AdaptiveIconDrawable(null, bitmapDrawable) else bitmapDrawable + val result = + if (icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) AdaptiveIconDrawable(null, bitmapDrawable) + else bitmapDrawable if (icon.hasTint()) { result.mutate() result.setTintList(icon.tintList) @@ -340,11 +377,13 @@ private fun loadIconDrawableWithTimeout( return result } -/* internal */ class SmartActionInflaterImpl @Inject constructor( +/* internal */ class SmartActionInflaterImpl +@Inject +constructor( private val constants: SmartReplyConstants, private val activityStarter: ActivityStarter, private val smartReplyController: SmartReplyController, - private val headsUpManager: HeadsUpManager + private val headsUpManager: HeadsUpManager, ) : SmartActionInflater { override fun inflateActionButton( @@ -354,17 +393,18 @@ private fun loadIconDrawableWithTimeout( actionIndex: Int, action: Notification.Action, delayOnClickListener: Boolean, - packageContext: Context + packageContext: Context, ): Button = - (LayoutInflater.from(parent.context) - .inflate(R.layout.smart_action_button, parent, false) as Button - ).apply { + (LayoutInflater.from(parent.context).inflate(R.layout.smart_action_button, parent, false) + as Button) + .apply { text = action.title - // We received the Icon from the application - so use the Context of the application to + // We received the Icon from the application - so use the Context of the application + // to // reference icon resources. - val newIconSize = context.resources - .getDimensionPixelSize(R.dimen.smart_action_button_icon_size) + val newIconSize = + context.resources.getDimensionPixelSize(R.dimen.smart_action_button_icon_size) val iconDrawable = loadIconDrawableWithTimeout(action.getIcon(), packageContext, newIconSize) ?: GradientDrawable() @@ -372,13 +412,15 @@ private fun loadIconDrawableWithTimeout( // Add the action icon to the Smart Action button. setCompoundDrawablesRelative(iconDrawable, null, null, null) - val onClickListener = View.OnClickListener { - onSmartActionClick(entry, smartActions, actionIndex, action) - } + val onClickListener = + View.OnClickListener { + onSmartActionClick(entry, smartActions, actionIndex, action) + } setOnClickListener( - if (delayOnClickListener) - DelayedOnClickListener(onClickListener, constants.onClickInitDelay) - else onClickListener) + if (delayOnClickListener) + DelayedOnClickListener(onClickListener, constants.onClickInitDelay) + else onClickListener + ) // Mark this as an Action button (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.ACTION @@ -388,18 +430,31 @@ private fun loadIconDrawableWithTimeout( entry: NotificationEntry, smartActions: SmartActions, actionIndex: Int, - action: Notification.Action + action: Notification.Action, ) = - if (smartActions.fromAssistant && - SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction) { - entry.row.doSmartActionClick(entry.row.x.toInt() / 2, - entry.row.y.toInt() / 2, SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY) - smartReplyController - .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant) + if ( + smartActions.fromAssistant && + SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction + ) { + entry.row.doSmartActionClick( + entry.row.x.toInt() / 2, + entry.row.y.toInt() / 2, + SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY, + ) + smartReplyController.smartActionClicked( + entry, + actionIndex, + action, + smartActions.fromAssistant, + ) } else { activityStarter.startPendingIntentDismissingKeyguard(action.actionIntent, entry.row) { - smartReplyController - .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant) + smartReplyController.smartActionClicked( + entry, + actionIndex, + action, + smartActions.fromAssistant, + ) } } } @@ -411,16 +466,18 @@ interface SmartReplyInflater { smartReplies: SmartReplies, replyIndex: Int, choice: CharSequence, - delayOnClickListener: Boolean + delayOnClickListener: Boolean, ): Button } -class SmartReplyInflaterImpl @Inject constructor( +class SmartReplyInflaterImpl +@Inject +constructor( private val constants: SmartReplyConstants, private val keyguardDismissUtil: KeyguardDismissUtil, private val remoteInputManager: NotificationRemoteInputManager, private val smartReplyController: SmartReplyController, - private val context: Context + private val context: Context, ) : SmartReplyInflater { override fun inflateReplyButton( @@ -429,37 +486,35 @@ class SmartReplyInflaterImpl @Inject constructor( smartReplies: SmartReplies, replyIndex: Int, choice: CharSequence, - delayOnClickListener: Boolean + delayOnClickListener: Boolean, ): Button = - (LayoutInflater.from(parent.context) - .inflate(R.layout.smart_reply_button, parent, false) as Button - ).apply { + (LayoutInflater.from(parent.context).inflate(R.layout.smart_reply_button, parent, false) + as Button) + .apply { text = choice - val onClickListener = View.OnClickListener { - onSmartReplyClick( - entry, - smartReplies, - replyIndex, - parent, - this, - choice) - } + val onClickListener = + View.OnClickListener { + onSmartReplyClick(entry, smartReplies, replyIndex, parent, this, choice) + } setOnClickListener( - if (delayOnClickListener) - DelayedOnClickListener(onClickListener, constants.onClickInitDelay) - else onClickListener) - accessibilityDelegate = object : View.AccessibilityDelegate() { - override fun onInitializeAccessibilityNodeInfo( - host: View, - info: AccessibilityNodeInfo - ) { - super.onInitializeAccessibilityNodeInfo(host, info) - val label = parent.resources - .getString(R.string.accessibility_send_smart_reply) - val action = AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label) - info.addAction(action) + if (delayOnClickListener) + DelayedOnClickListener(onClickListener, constants.onClickInitDelay) + else onClickListener + ) + accessibilityDelegate = + object : View.AccessibilityDelegate() { + override fun onInitializeAccessibilityNodeInfo( + host: View, + info: AccessibilityNodeInfo, + ) { + super.onInitializeAccessibilityNodeInfo(host, info) + val label = + parent.resources.getString(R.string.accessibility_send_smart_reply) + val action = + AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label) + info.addAction(action) + } } - } // TODO: probably shouldn't do this here, bad API // Mark this as a Reply button (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.REPLY @@ -471,39 +526,52 @@ class SmartReplyInflaterImpl @Inject constructor( replyIndex: Int, smartReplyView: SmartReplyView, button: Button, - choice: CharSequence - ) = keyguardDismissUtil.executeWhenUnlocked(!entry.isRowPinned) { - val canEditBeforeSend = constants.getEffectiveEditChoicesBeforeSending( - smartReplies.remoteInput.editChoicesBeforeSending) - if (canEditBeforeSend) { - remoteInputManager.activateRemoteInput( + choice: CharSequence, + ) = + keyguardDismissUtil.executeWhenUnlocked(!entry.isRowPinned) { + val canEditBeforeSend = + constants.getEffectiveEditChoicesBeforeSending( + smartReplies.remoteInput.editChoicesBeforeSending + ) + if (canEditBeforeSend) { + remoteInputManager.activateRemoteInput( button, arrayOf(smartReplies.remoteInput), smartReplies.remoteInput, smartReplies.pendingIntent, - NotificationEntry.EditedSuggestionInfo(choice, replyIndex)) - } else { - smartReplyController.smartReplySent( + NotificationEntry.EditedSuggestionInfo(choice, replyIndex), + ) + } else { + smartReplyController.smartReplySent( entry, replyIndex, button.text, NotificationLogger.getNotificationLocation(entry).toMetricsEventEnum(), - false /* modifiedBeforeSending */) - entry.setHasSentReply() - try { - val intent = createRemoteInputIntent(smartReplies, choice) - val opts = ActivityOptions.makeBasic() - opts.setPendingIntentBackgroundActivityStartMode( - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) - smartReplies.pendingIntent.send(context, 0, intent, /* onFinished */null, - /* handler */ null, /* requiredPermission */ null, opts.toBundle()) - } catch (e: PendingIntent.CanceledException) { - Log.w(TAG, "Unable to send smart reply", e) + false, /* modifiedBeforeSending */ + ) + entry.setHasSentReply() + try { + val intent = createRemoteInputIntent(smartReplies, choice) + val opts = ActivityOptions.makeBasic() + opts.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED + ) + smartReplies.pendingIntent.send( + context, + 0, + intent, /* onFinished */ + null, + /* handler */ null, /* requiredPermission */ + null, + opts.toBundle(), + ) + } catch (e: PendingIntent.CanceledException) { + Log.w(TAG, "Unable to send smart reply", e) + } + smartReplyView.hideSmartSuggestions() } - smartReplyView.hideSmartSuggestions() + false // do not defer } - false // do not defer - } private fun createRemoteInputIntent(smartReplies: SmartReplies, choice: CharSequence): Intent { val results = Bundle() @@ -516,12 +584,11 @@ class SmartReplyInflaterImpl @Inject constructor( } /** - * An OnClickListener wrapper that blocks the underlying OnClickListener for a given amount of - * time. + * An OnClickListener wrapper that blocks the underlying OnClickListener for a given amount of time. */ private class DelayedOnClickListener( private val mActualListener: View.OnClickListener, - private val mInitDelayMs: Long + private val mInitDelayMs: Long, ) : View.OnClickListener { private val mInitTimeMs = SystemClock.elapsedRealtime() @@ -535,7 +602,7 @@ private class DelayedOnClickListener( } private fun hasFinishedInitialization(): Boolean = - SystemClock.elapsedRealtime() >= mInitTimeMs + mInitDelayMs + SystemClock.elapsedRealtime() >= mInitTimeMs + mInitDelayMs } private const val TAG = "SmartReplyViewInflater" @@ -544,12 +611,12 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) // convenience function that swaps parameter order so that lambda can be placed at the end private fun KeyguardDismissUtil.executeWhenUnlocked( requiresShadeOpen: Boolean, - onDismissAction: () -> Boolean + onDismissAction: () -> Boolean, ) = executeWhenUnlocked(onDismissAction, requiresShadeOpen, false) // convenience function that swaps parameter order so that lambda can be placed at the end private fun ActivityStarter.startPendingIntentDismissingKeyguard( intent: PendingIntent, associatedView: View?, - runnable: () -> Unit -) = startPendingIntentDismissingKeyguard(intent, runnable::invoke, associatedView)
\ No newline at end of file + runnable: () -> Unit, +) = startPendingIntentDismissingKeyguard(intent, runnable::invoke, associatedView) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt index bcf4bad9991d..45fdd21e795e 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt @@ -63,7 +63,8 @@ fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni private fun rememberHomeGestureRecognizer(resources: Resources): GestureRecognizer { val distance = resources.getDimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold) - return remember(distance) { HomeGestureRecognizer(distance) } + val velocity = resources.getDimension(R.dimen.touchpad_home_gesture_velocity_threshold) + return remember(distance) { HomeGestureRecognizer(distance, velocity) } } @Composable diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt index e10b8253ebad..9801626dac8f 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt @@ -19,9 +19,14 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.util.MathUtils import android.view.MotionEvent import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress +import kotlin.math.abs /** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */ -class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer { +class HomeGestureRecognizer( + private val gestureDistanceThresholdPx: Int, + private val velocityThresholdPxPerMs: Float, + private val velocityTracker: VelocityTracker = VerticalVelocityTracker(), +) : GestureRecognizer { private val distanceTracker = DistanceTracker() private var gestureStateChangedCallback: (GestureState) -> Unit = {} @@ -37,10 +42,14 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) + velocityTracker.accept(event) updateGestureState( gestureStateChangedCallback, gestureState, - isFinished = { -it.deltaY >= gestureDistanceThresholdPx }, + isFinished = { + -it.deltaY >= gestureDistanceThresholdPx && + abs(velocityTracker.calculateVelocity().value) >= velocityThresholdPxPerMs + }, progress = { InProgress(MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx)) }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt index c47888609a61..5ff583a19ee9 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt @@ -28,10 +28,10 @@ import kotlin.math.abs class RecentAppsGestureRecognizer( private val gestureDistanceThresholdPx: Int, private val velocityThresholdPxPerMs: Float, - private val distanceTracker: DistanceTracker = DistanceTracker(), - private val velocityTracker: VerticalVelocityTracker = VerticalVelocityTracker(), + private val velocityTracker: VelocityTracker = VerticalVelocityTracker(), ) : GestureRecognizer { + private val distanceTracker = DistanceTracker() private var gestureStateChangedCallback: (GestureState) -> Unit = {} override fun addGestureStateCallback(callback: (GestureState) -> Unit) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java index bf13ceb5666a..4bc5f2303ee8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java @@ -45,6 +45,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; @@ -56,6 +58,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.logging.CarrierTextManagerLogger; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.kosmos.KosmosJavaAdapter; @@ -200,6 +203,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testAirplaneMode() { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); reset(mCarrierTextCallback); @@ -220,8 +224,32 @@ public class CarrierTextManagerTest extends SysuiTestCase { assertEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText); } + @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testAirplaneMode_flagEnabled() { + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(0)).thenReturn( + TelephonyManager.SIM_STATE_READY); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + mCarrierTextManager.updateCarrierText(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText); + } + /** regression test for b/281706473, caused by sending NULL plmn / spn to the logger */ @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testAirplaneMode_noSim_nullPlmn_nullSpn_doesNotCrash() { // GIVEN - sticy broadcast that returns a null PLMN and null SPN Intent stickyIntent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); @@ -263,7 +291,52 @@ public class CarrierTextManagerTest extends SysuiTestCase { // No assert, this test should not crash } + /** regression test for b/281706473, caused by sending NULL plmn / spn to the logger */ + @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testAirplaneMode_noSim_nullPlmn_nullSpn_doesNotCrash_flagEnabled() { + // GIVEN - sticy broadcast that returns a null PLMN and null SPN + Intent stickyIntent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); + stickyIntent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, true); + stickyIntent.removeExtra(TelephonyManager.EXTRA_PLMN); + stickyIntent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, true); + stickyIntent.removeExtra(TelephonyManager.EXTRA_SPN); + + mCarrierTextManager = new CarrierTextManager.Builder( + getContextSpyForStickyBroadcast(stickyIntent), + mContext.getResources(), + mWifiRepository, + mSatelliteViewModel, + mJavaAdapter, + mTelephonyManager, + mTelephonyListenerManager, + mWakefulnessLifecycle, + mMainExecutor, + mBgExecutor, + mKeyguardUpdateMonitor, + mLogger + ) + .setShowAirplaneMode(true) + .setShowMissingSim(true) + .build(); + + // GIVEN - airplane mode is off (causing CTM to fetch the sticky broadcast) + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(0)) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN CTM fetches the broadcast and attempts to log the result, no crash results + mCarrierTextManager.updateCarrierText(); + + // No assert, this test should not crash + } + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCardIOError() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -299,6 +372,44 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCardIOError_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(0)).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(1)).thenReturn( + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + mCarrierTextManager.mCallback.onSimStateChanged(3, 1, + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertEquals("TEST_CARRIER" + SEPARATOR + INVALID_CARD_TEXT, captor.getValue().carrierText); + // There's only one subscription in the list + assertEquals(1, captor.getValue().listOfCarriers.length); + assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]); + + // Now it becomes single SIM active mode. + reset(mCarrierTextCallback); + when(mTelephonyManager.getActiveModemCount()).thenReturn(1); + // Update carrier text. It should ignore error state of subId 3 in inactive slotId. + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertEquals("TEST_CARRIER", captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testWrongSlots() { reset(mCarrierTextCallback); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( @@ -313,6 +424,22 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testWrongSlots_flagEnabled() { + reset(mCarrierTextCallback); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( + new ArrayList<>()); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + // This should not produce an out of bounds error, even though there are no subscriptions + mCarrierTextManager.mCallback.onSimStateChanged(0, -3, + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + mCarrierTextManager.mCallback.onSimStateChanged(0, 3, TelephonyManager.SIM_STATE_READY); + verify(mCarrierTextCallback, never()).updateCarrierInfo(any()); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testMoreSlotsThanSubs() { reset(mCarrierTextCallback); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( @@ -335,6 +462,29 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testMoreSlotsThanSubs_flagEnabled() { + reset(mCarrierTextCallback); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( + new ArrayList<>()); + + // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the + // same answer as KeyguardUpdateMonitor. Remove when this is addressed + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn( + new ArrayList<>()); + + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + // This should not produce an out of bounds error, even though there are no subscriptions + mCarrierTextManager.mCallback.onSimStateChanged(0, 1, + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo( + any(CarrierTextManager.CarrierTextCallbackInfo.class)); + } + + @Test public void testCallback() { reset(mCarrierTextCallback); mCarrierTextManager.postToCallback(mCarrierTextCallbackInfo); @@ -360,6 +510,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCreateInfo_OneValidSubscription() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -385,6 +536,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCreateInfo_OneValidSubscription_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + CarrierTextManager.CarrierTextCallbackInfo info = captor.getValue(); + assertEquals(1, info.listOfCarriers.length); + assertEquals(TEST_CARRIER, info.listOfCarriers[0]); + assertEquals(1, info.subscriptionIds.length); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCreateInfo_OneValidSubscriptionWithRoaming() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -410,6 +588,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCreateInfo_OneValidSubscriptionWithRoaming_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION_ROAMING); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + CarrierTextManager.CarrierTextCallbackInfo info = captor.getValue(); + assertEquals(1, info.listOfCarriers.length); + assertTrue(info.listOfCarriers[0].toString().contains(TEST_CARRIER)); + assertEquals(1, info.subscriptionIds.length); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_noTextOnReadySimWhenNull() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -434,6 +639,32 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_noTextOnReadySimWhenNull_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION_NULL); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertTrue("Carrier text should be empty, instead it's " + captor.getValue().carrierText, + TextUtils.isEmpty(captor.getValue().carrierText)); + assertFalse("No SIM should be available", captor.getValue().anySimReady); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_noTextOnReadySimWhenNull_airplaneMode_wifiOn() { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); reset(mCarrierTextCallback); @@ -472,6 +703,46 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_noTextOnReadySimWhenNull_airplaneMode_wifiOn_flagEnabled() { + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION_NULL); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + assertFalse(mWifiRepository.isWifiConnectedWithValidSsid()); + mWifiRepository.setWifiNetwork( + WifiNetworkModel.Active.Companion.of( + /* isValidated= */ false, + /* level= */ 0, + /* ssid= */ "", + /* hotspotDeviceType= */ WifiNetworkModel.HotspotDeviceType.NONE)); + assertTrue(mWifiRepository.isWifiConnectedWithValidSsid()); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + ServiceState ss = mock(ServiceState.class); + when(ss.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE); + mKeyguardUpdateMonitor.mServiceStates.put(TEST_SUBSCRIPTION_NULL.getSubscriptionId(), ss); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertFalse("No SIM should be available", captor.getValue().anySimReady); + // There's no airplane mode if at least one SIM is State.READY and there's wifi + assertFalse("Device should not be in airplane mode", captor.getValue().airplaneMode); + assertNotEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_satelliteTextNull_isSatelliteFalse_textNotUsed() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -497,6 +768,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_satelliteTextNull_isSatelliteFalse_textNotUsed_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN the satellite text is null + mSatelliteViewModel.getCarrierText().setValue(null); + mTestScope.getTestScheduler().runCurrent(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + + // THEN satellite mode is false and the default subscription carrier text is used + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isFalse(); + assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_hasSatelliteText_isSatelliteTrue_textUsed() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -522,6 +820,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_hasSatelliteText_isSatelliteTrue_textUsed_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN the satellite text is non-null + mSatelliteViewModel.getCarrierText().setValue("Satellite Test Text"); + mTestScope.getTestScheduler().runCurrent(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + + // THEN satellite mode is true and the satellite text is used + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isTrue(); + assertThat(captor.getValue().carrierText).isEqualTo("Satellite Test Text"); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_satelliteTextUpdates_autoTriggersCallback() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -559,6 +884,45 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_satelliteTextUpdates_autoTriggersCallback_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN the satellite text is set + mSatelliteViewModel.getCarrierText().setValue("Test satellite text"); + mTestScope.getTestScheduler().runCurrent(); + + // THEN we should automatically re-trigger #updateCarrierText and get callback info + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + // AND use the satellite text as the carrier text + assertThat(captor.getValue().isInSatelliteMode).isTrue(); + assertThat(captor.getValue().carrierText).isEqualTo("Test satellite text"); + + // WHEN the satellite text is reset to null + reset(mCarrierTextCallback); + mSatelliteViewModel.getCarrierText().setValue(null); + mTestScope.getTestScheduler().runCurrent(); + + // THEN we should automatically re-trigger #updateCarrierText and get callback info + // that doesn't include the satellite info + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isFalse(); + assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_updatedWhileNotListening_getsNewValueWhenListening() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -602,6 +966,50 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_updatedWhileNotListening_getsNewValueWhenListening_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + mSatelliteViewModel.getCarrierText().setValue("Old satellite text"); + mTestScope.getTestScheduler().runCurrent(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().carrierText).isEqualTo("Old satellite text"); + + // WHEN we stop listening + reset(mCarrierTextCallback); + mCarrierTextManager.setListening(null); + + // AND the satellite text updates + mSatelliteViewModel.getCarrierText().setValue("New satellite text"); + + // THEN we don't get new callback info because we aren't listening + verify(mCarrierTextCallback, never()).updateCarrierInfo(any()); + + // WHEN we start listening again + reset(mCarrierTextCallback); + mCarrierTextManager.setListening(mCarrierTextCallback); + + // THEN we should automatically re-trigger #updateCarrierText and get callback info + // that includes the new satellite state and text + mTestScope.getTestScheduler().runCurrent(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isTrue(); + assertThat(captor.getValue().carrierText).isEqualTo("New satellite text"); + } + + @Test public void testCreateInfo_noSubscriptions() { reset(mCarrierTextCallback); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( @@ -622,6 +1030,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_oneValidSubscription() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -644,6 +1053,30 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_oneValidSubscription_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_twoValidSubscriptions() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -668,6 +1101,32 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_twoValidSubscriptions_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_oneDisabledSub() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -693,6 +1152,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_oneDisabledSub_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_firstDisabledSub() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -718,6 +1204,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_firstDisabledSub_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY) + .thenReturn(TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_threeSubsMiddleDisabled() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -744,6 +1257,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_threeSubsMiddleDisabled_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY) + .thenReturn(TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test public void testGetStatusForIccState() { when(mKeyguardUpdateMonitor.isDeviceProvisioned()).thenReturn(false); assertEquals(CarrierTextManager.StatusMode.SimMissingLocked, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index a39ca5de787d..839a2bdf5588 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -103,6 +103,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.service.dreams.IDreamManager; @@ -200,8 +201,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private static final String TEST_CARRIER_2 = "TEST_CARRIER_2"; private static final int TEST_CARRIER_ID = 1; private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24"; - private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0, - TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "", + private static final int TEST_SLOT_ID = 3; + private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", + TEST_SLOT_ID, TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID, TEST_CARRIER_ID, 0); private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0, @@ -483,7 +485,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testSimStateInitialized() { + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testSimStateInitialized_flagDisabled() { cleanupKeyguardUpdateMonitor(); final int subId = 3; final int state = TelephonyManager.SIM_STATE_ABSENT; @@ -500,6 +503,24 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testSimStateInitialized_flagEnabled() { + cleanupKeyguardUpdateMonitor(); + final int state = TelephonyManager.SIM_STATE_ABSENT; + final int slotId = 0; + final int subId = 3; + when(mTelephonyManager.getActiveModemCount()).thenReturn(1); + when(mTelephonyManager.getSimState(anyInt())).thenReturn(state); + when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[]{subId}); + + KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mContext); + + mTestableLooper.processAllMessages(); + + assertThat(testKUM.getSimStateForSlotId(slotId)).isEqualTo(state); + } + + @Test public void testIgnoresSimStateCallback_rebroadcast() { Intent intent = defaultSimStateChangedIntent(); @@ -1240,7 +1261,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testActiveSubscriptionBecomesInactive() { + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testActiveSubscriptionBecomesInactive_flagDisabled() { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION); when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list); @@ -1263,6 +1285,28 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testActiveSubscriptionBecomesInactive_flagEnabled() { + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list); + mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged( + TEST_SUBSCRIPTION.getSubscriptionId()); + mTestableLooper.processAllMessages(); + assertThat(mKeyguardUpdateMonitor.mSimDatasBySlotId.get(TEST_SLOT_ID)) + .isNotNull(); + + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()) + .thenReturn(new ArrayList<>()); + mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged( + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mTestableLooper.processAllMessages(); + + assertThat(mKeyguardUpdateMonitor.mSimDatasBySlotId.get(TEST_SLOT_ID)) + .isNull(); + } + + @Test public void testActiveSubscriptionList_filtersProvisioningNetworks() { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION_PROVISIONING); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index f41d5c8eeb23..8552e48a2024 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -63,8 +63,6 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.os.SystemClock; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.testing.TestableLooper; import android.testing.TestableResources; @@ -90,7 +88,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.kosmos.KosmosJavaAdapter; @@ -612,46 +609,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { .isEqualTo(expectedRatio); } - @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) - @Test - public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() { - int newSmallestScreenWidthDp = - mContext.getResources().getConfiguration().smallestScreenWidthDp * 2; - int windowFrameSize = mResources.getDimensionPixelSize( - com.android.internal.R.dimen.accessibility_window_magnifier_min_size); - Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize); - mSharedPreferences - .edit() - .putString(String.valueOf(newSmallestScreenWidthDp), - preferredWindowSize.toString()) - .commit(); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN, - Float.NaN); - }); - - // Screen density and size change - mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp; - final Rect testWindowBounds = new Rect( - mWindowManager.getCurrentWindowMetrics().getBounds()); - testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, - testWindowBounds.right + 100, testWindowBounds.bottom + 100); - mWindowManager.setWindowBounds(testWindowBounds); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); - }); - - // wait for rect update - waitForIdleSync(); - ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams(); - final int mirrorSurfaceMargin = mResources.getDimensionPixelSize( - R.dimen.magnification_mirror_surface_margin); - // The width and height of the view include the magnification frame and the margins. - assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin); - assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin); - } - - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierIndexAndWindow() { int newSmallestScreenWidthDp = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt index 75a5768193cf..75a5768193cf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt index a4653e736745..77db97710174 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt @@ -29,7 +29,6 @@ import com.google.common.truth.Truth.assertThat import org.junit.Test import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters -import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt index 4d138b488645..5d622eaeb1aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt @@ -122,9 +122,24 @@ class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT) + fun testOnClick_connectedAudioSharingMediaDevice_flagOff_previewOn_createDialog() { + with(kosmos) { + testScope.runTest { + whenever(BluetoothUtils.isAudioSharingPreviewEnabled(any())).thenReturn(true) + bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true) + actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog) + verify(dialogTransitionAnimator) + .showFromDialog(any(), any(), eq(null), anyBoolean()) + } + } + } + + @Test + @DisableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT) fun testOnClick_connectedAudioSharingMediaDevice_flagOff_shouldLaunchSettings() { with(kosmos) { testScope.runTest { + whenever(BluetoothUtils.isAudioSharingPreviewEnabled(any())).thenReturn(false) bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true) whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt index 2a6d29c61890..2a6d29c61890 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt index 90727b240caf..90727b240caf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt index feae901114e5..3bd249689da9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt @@ -413,6 +413,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { key("Ctrl") key("A") } + contentDescription { "$label, Press key Ctrl plus A" } } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index 32fa160fc29f..32fa160fc29f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java index eb1b44b75247..7ba797c03a0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java @@ -1467,4 +1467,66 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { verify(mInputRouteManager, never()).selectDevice(outputMediaDevice); verify(mLocalMediaManager, atLeastOnce()).connectDevice(outputMediaDevice); } + + @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void connectDeviceButton_presentAtAllTimesForNonGroupOutputs() { + mMediaSwitchingController.start(mCb); + reset(mCb); + + // Mock the selected output device. + doReturn(Collections.singletonList(mMediaDevice1)) + .when(mLocalMediaManager) + .getSelectedMediaDevice(); + + // Verify that there is initially one "Connect a device" button present. + assertThat(getNumberOfConnectDeviceButtons()).isEqualTo(1); + + // Change the selected device, and verify that there is still one "Connect a device" button + // present. + doReturn(Collections.singletonList(mMediaDevice2)) + .when(mLocalMediaManager) + .getSelectedMediaDevice(); + mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); + + assertThat(getNumberOfConnectDeviceButtons()).isEqualTo(1); + } + + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void connectDeviceButton_presentAtAllTimesForNonGroupOutputs_inputRoutingEnabled() { + mMediaSwitchingController.start(mCb); + reset(mCb); + + // Mock the selected output device. + doReturn(Collections.singletonList(mMediaDevice1)) + .when(mLocalMediaManager) + .getSelectedMediaDevice(); + + // Mock the selected input media device. + final MediaDevice selectedInputMediaDevice = mock(MediaDevice.class); + doReturn(selectedInputMediaDevice).when(mInputRouteManager).getSelectedInputDevice(); + + // Verify that there is initially one "Connect a device" button present. + assertThat(getNumberOfConnectDeviceButtons()).isEqualTo(1); + + // Change the selected device, and verify that there is still one "Connect a device" button + // present. + doReturn(Collections.singletonList(mMediaDevice2)) + .when(mLocalMediaManager) + .getSelectedMediaDevice(); + mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); + + assertThat(getNumberOfConnectDeviceButtons()).isEqualTo(1); + } + + private int getNumberOfConnectDeviceButtons() { + int numberOfConnectDeviceButtons = 0; + for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { + if (item.getMediaItemType() == MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE) { + numberOfConnectDeviceButtons++; + } + } + return numberOfConnectDeviceButtons; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java index bc3c0d96fa22..d59a404b15bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java @@ -49,6 +49,7 @@ import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.views.NavigationBar; import com.android.systemui.recents.OverviewProxyService; @@ -56,7 +57,7 @@ import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.AutoHideControllerStore; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -89,6 +90,11 @@ public class NavigationBarControllerImplTest extends SysuiTestCase { private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + + private final AutoHideControllerStore mAutoHideControllerStore = + mKosmos.getAutoHideControllerStore(); + @Mock private CommandQueue mCommandQueue; @Mock @@ -113,7 +119,7 @@ public class NavigationBarControllerImplTest extends SysuiTestCase { mTaskbarDelegate, mNavigationBarFactory, mock(DumpManager.class), - mock(AutoHideController.class), + mAutoHideControllerStore, mock(LightBarController.class), TaskStackChangeListeners.getTestInstance(), Optional.of(mock(Pip.class)), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt index fac5ecb49027..f23553eda3b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.click import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.performClick @@ -73,7 +74,7 @@ class ResizingTest : SysuiTestCase() { } @Test - fun toggleIconTile_shouldBeLarge() { + fun toggleIconTileWithA11yAction_shouldBeLarge() { var tiles by mutableStateOf(TestEditTiles) val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { @@ -89,7 +90,7 @@ class ResizingTest : SysuiTestCase() { } @Test - fun toggleLargeTile_shouldBeIcon() { + fun toggleLargeTileWithA11yAction_shouldBeIcon() { var tiles by mutableStateOf(TestEditTiles) val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { @@ -105,7 +106,7 @@ class ResizingTest : SysuiTestCase() { } @Test - fun resizedLarge_shouldBeIcon() { + fun tapOnIconResizingHandle_shouldBeLarge() { var tiles by mutableStateOf(TestEditTiles) val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { @@ -116,12 +117,32 @@ class ResizingTest : SysuiTestCase() { composeRule .onNodeWithContentDescription("tileA") .performClick() // Select - .performTouchInput { // Resize down - swipeRight() + .performTouchInput { // Tap on resizing handle + click(centerRight) + } + composeRule.waitForIdle() + + assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) + } + + @Test + fun tapOnLargeResizingHandle_shouldBeIcon() { + var tiles by mutableStateOf(TestEditTiles) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) + composeRule.setContent { + EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } + } + composeRule.waitForIdle() + + composeRule + .onNodeWithContentDescription("tileD_large") + .performClick() // Select + .performTouchInput { // Tap on resizing handle + click(centerRight) } composeRule.waitForIdle() - assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(1) + assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1) } @Test @@ -134,6 +155,26 @@ class ResizingTest : SysuiTestCase() { composeRule.waitForIdle() composeRule + .onNodeWithContentDescription("tileA") + .performClick() // Select + .performTouchInput { // Resize up + swipeRight(startX = right, endX = right * 2) + } + composeRule.waitForIdle() + + assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) + } + + @Test + fun resizedLarge_shouldBeIcon() { + var tiles by mutableStateOf(TestEditTiles) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) + composeRule.setContent { + EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } + } + composeRule.waitForIdle() + + composeRule .onNodeWithContentDescription("tileD_large") .performClick() // Select .performTouchInput { // Resize down diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 859f84edda39..e7fb470cfa76 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -33,6 +33,7 @@ import androidx.lifecycle.LifecycleOwner import androidx.test.filters.SmallTest import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX import com.android.systemui.SysuiTestCase import com.android.systemui.ambient.touch.TouchHandler @@ -630,6 +631,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } + @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) @Test fun onTouchEvent_shadeInteracting_movesNotDispatched() = with(kosmos) { @@ -686,6 +688,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } + @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) @Test fun onTouchEvent_bouncerInteracting_movesNotDispatched() = with(kosmos) { @@ -718,6 +721,19 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } + @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) + @Test + fun onTouchEvent_onLockscreenAndGlanceableHubV2_touchIgnored() = + with(kosmos) { + testScope.runTest { + // On lockscreen. + goToScene(CommunalScenes.Blank) + + assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + verify(containerView, never()).onTouchEvent(DOWN_EVENT) + } + } + @Test fun disposeView_destroysTouchMonitor() { clearInvocations(touchMonitor) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 041d1a611b55..041d1a611b55 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index 61d14b73204a..58864f635190 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -76,7 +76,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.FakeEventLog; import com.android.systemui.util.settings.FakeGlobalSettings; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index f870200c2b00..28577b336b6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -61,7 +61,7 @@ import com.android.systemui.statusbar.notification.logging.nano.Notifications; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index d8a23d6bd32e..5aee92939ed5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.FeedbackIcon; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; @@ -394,7 +395,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { ExpandableNotificationRow row = mNotificationTestHelper.createRow(); AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); row.setAboveShelfChangedListener(listener); - row.setPinned(true); + row.setPinnedStatus(PinnedStatus.PinnedBySystem); verify(listener).onAboveShelfStateChanged(true); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 723c0d701305..2d35ea5d4e95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -65,11 +65,11 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.statusbar.policy.deviceProvisionedController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.wmshell.BubblesManager 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 d2350bc82667..11b19f95c1c0 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 @@ -103,7 +103,7 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.policy.AvalancheController; +import com.android.systemui.statusbar.notification.headsup.AvalancheController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor; 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 c9f8463d510c..6912eda3c3d4 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 @@ -198,7 +198,7 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -334,7 +334,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private CentralSurfacesCommandQueueCallbacks mCentralSurfacesCommandQueueCallbacks; @Mock private PluginManager mPluginManager; @Mock private ViewMediatorCallback mViewMediatorCallback; - @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock private ShadeTouchableRegionManager mShadeTouchableRegionManager; @Mock private PluginDependencyProvider mPluginDependencyProvider; @Mock private ExtensionController mExtensionController; @Mock private UserInfoControllerImpl mUserInfoControllerImpl; @@ -617,7 +617,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mKeyguardIndicationController, mDemoModeController, mNotificationShadeDepthControllerLazy, - mStatusBarTouchableRegionManager, + mShadeTouchableRegionManager, mBrightnessSliderFactory, mScreenOffAnimationController, mWallpaperController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index cace60ce4c0e..2f30b745a4a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -95,7 +95,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt index 3d7603368ebc..3d7603368ebc 100644 --- a/packages/SystemUI/multivalentTests/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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index a213c85eaa62..1d4b8e14695d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import kotlin.sequences.Sequence; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt index 1184a76d54ff..1184a76d54ff 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 5d4d9e698161..a1c9022e18bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -141,6 +141,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.notification.interruption.AvalancheProvider; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; @@ -154,7 +155,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.ZenModeController; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt index 50631409308c..0ba7c8574d85 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt @@ -62,6 +62,7 @@ import com.android.systemui.statusbar.NotificationShadeDepthController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.NotifCollection +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController @@ -74,7 +75,6 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.ZenModeController import com.android.systemui.statusbar.window.StatusBarWindowController @@ -173,7 +173,7 @@ data class TestMocksModule( interface Bindings { @Binds fun bindStatusBarStateController( - sysuiStatusBarStateController: SysuiStatusBarStateController, + sysuiStatusBarStateController: SysuiStatusBarStateController ): StatusBarStateController } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MotionEventHelper.java b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/MotionEventHelper.java index 550e77d63c3b..550e77d63c3b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MotionEventHelper.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/MotionEventHelper.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/TestableWindowManager.java index 859517839388..859517839388 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/TestableWindowManager.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/TestableWindowManager.java diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryKosmos.kt new file mode 100644 index 000000000000..a6e71333c816 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryKosmos.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.activity.data.repository + +import android.app.activityManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.log.core.Logger +import kotlinx.coroutines.flow.MutableStateFlow + +val Kosmos.activityManagerRepository by Kosmos.Fixture { FakeActivityManagerRepository() } + +val Kosmos.realActivityManagerRepository by + Kosmos.Fixture { ActivityManagerRepositoryImpl(testDispatcher, activityManager) } + +class FakeActivityManagerRepository : ActivityManagerRepository { + private val uidFlows = mutableMapOf<Int, MutableList<MutableStateFlow<Boolean>>>() + + var startingIsAppVisibleValue = false + + override fun createIsAppVisibleFlow( + creationUid: Int, + logger: Logger, + identifyingLogTag: String, + ): MutableStateFlow<Boolean> { + val newFlow = MutableStateFlow(startingIsAppVisibleValue) + uidFlows.computeIfAbsent(creationUid) { mutableListOf() }.add(newFlow) + return newFlow + } + + fun setIsAppVisible(uid: Int, isAppVisible: Boolean) { + uidFlows[uid]?.forEach { stateFlow -> stateFlow.value = isAppVisible } + } +} + +val ActivityManagerRepository.fake + get() = this as FakeActivityManagerRepository diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt index 4f4d1da42303..e0c0fbd7f033 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt @@ -16,12 +16,14 @@ package com.android.systemui.bluetooth.qsdialog +import android.content.applicationContext import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher val Kosmos.audioSharingInteractor: AudioSharingInteractor by Kosmos.Fixture { AudioSharingInteractorImpl( + applicationContext, localBluetoothManager, bluetoothTileDialogAudioSharingRepository, testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt index e0d1d16bcf55..72541540226c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt @@ -28,6 +28,7 @@ import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer import com.android.systemui.haptics.msdl.bouncerHapticPlayer import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardDismissActionInteractor import com.android.systemui.keyguard.domain.interactor.keyguardMediaKeyInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -63,6 +64,7 @@ val Kosmos.bouncerSceneContentViewModel by Fixture { bouncerHapticPlayer = bouncerHapticPlayer, keyguardMediaKeyInteractor = keyguardMediaKeyInteractor, bouncerActionButtonInteractor = bouncerActionButtonInteractor, + keyguardDismissActionInteractor = keyguardDismissActionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt index 5485f79629e8..5da6c7b1f2a8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.communal.data.repository import android.app.admin.devicePolicyManager +import android.content.res.mainResources import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.kosmos.Kosmos @@ -27,6 +28,7 @@ val Kosmos.communalSettingsRepository: CommunalSettingsRepository by Kosmos.Fixture { CommunalSettingsRepositoryImpl( bgDispatcher = testDispatcher, + resources = mainResources, featureFlagsClassic = featureFlagsClassic, secureSettings = fakeSettings, broadcastDispatcher = broadcastDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt index 78ea70086605..ddcc92691f5b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt @@ -57,6 +57,10 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository { addDisplay(display(type, id = displayId)) } + suspend fun addDisplays(vararg displays: Display) { + displays.forEach { addDisplay(it) } + } + suspend fun addDisplay(display: Display) { flow.value += display displayAdditionEventFlow.emit(display) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorKosmos.kt new file mode 100644 index 000000000000..c2466bb4c14d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.domain.interactor + +import com.android.systemui.display.data.repository.displayWindowPropertiesRepository +import com.android.systemui.kosmos.Kosmos + +val Kosmos.displayWindowPropertiesInteractor by + Kosmos.Fixture { DisplayWindowPropertiesInteractorImpl(displayWindowPropertiesRepository) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeServiceFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/doze/DozeServiceFake.java index f55c2b77ca0f..f55c2b77ca0f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeServiceFake.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/doze/DozeServiceFake.java diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index 2c8581608739..e7672ffabc79 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -55,10 +55,10 @@ var Kosmos.shortcutHelperAppCategoriesShortcutsSource: KeyboardShortcutGroupsSou Kosmos.Fixture { AppCategoriesShortcutsSource(windowManager, testDispatcher) } var Kosmos.shortcutHelperSystemShortcutsSource: KeyboardShortcutGroupsSource by - Kosmos.Fixture { SystemShortcutsSource(mainResources) } + Kosmos.Fixture { SystemShortcutsSource(mainResources, fakeInputManager.inputManager) } var Kosmos.shortcutHelperMultiTaskingShortcutsSource: KeyboardShortcutGroupsSource by - Kosmos.Fixture { MultitaskingShortcutsSource(mainResources) } + Kosmos.Fixture { MultitaskingShortcutsSource(mainResources, applicationContext) } val Kosmos.shortcutHelperStateRepository by Kosmos.Fixture { @@ -72,7 +72,9 @@ val Kosmos.shortcutHelperStateRepository by } var Kosmos.shortcutHelperInputShortcutsSource: KeyboardShortcutGroupsSource by - Kosmos.Fixture { InputShortcutsSource(mainResources, windowManager) } + Kosmos.Fixture { + InputShortcutsSource(mainResources, windowManager, fakeInputManager.inputManager) + } var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) } @@ -141,7 +143,10 @@ val Kosmos.shortcutHelperStateInteractor by val Kosmos.shortcutHelperCategoriesInteractor by Kosmos.Fixture { - ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) { + ShortcutHelperCategoriesInteractor( + context = applicationContext, + defaultShortcutCategoriesRepository, + ) { customShortcutCategoriesRepository } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt index 548b5646f5d4..548b5646f5d4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt index bd841ab2e801..09f5fd79eeca 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt @@ -17,14 +17,12 @@ package com.android.systemui.keyguard.domain.interactor import com.android.keyguard.logging.KeyguardLogger -import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer -import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -38,8 +36,6 @@ val Kosmos.keyguardDismissActionInteractor by dismissInteractor = keyguardDismissInteractor, applicationScope = testScope.backgroundScope, deviceUnlockedInteractor = { deviceUnlockedInteractor }, - powerInteractor = powerInteractor, - alternateBouncerInteractor = alternateBouncerInteractor, shadeInteractor = { shadeInteractor }, keyguardInteractor = { keyguardInteractor }, sceneInteractor = { sceneInteractor }, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt index 007d2297e387..f88ed07046ab 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt @@ -16,18 +16,24 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.internal.widget.lockPatternUtils import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.user.domain.interactor.selectedUserInteractor val Kosmos.keyguardEnabledInteractor by Kosmos.Fixture { KeyguardEnabledInteractor( applicationCoroutineScope, + testDispatcher, keyguardRepository, biometricSettingsRepository, - keyguardDismissTransitionInteractor, + selectedUserInteractor, + lockPatternUtils, + { keyguardDismissTransitionInteractor }, internalTransitionInteractor = internalKeyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt index 39236c7c9fae..2423949cdacc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt @@ -24,5 +24,6 @@ val Kosmos.keyguardLockWhileAwakeInteractor by KeyguardLockWhileAwakeInteractor( biometricSettingsRepository = biometricSettingsRepository, keyguardEnabledInteractor = keyguardEnabledInteractor, + keyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt new file mode 100644 index 000000000000..29335c590a75 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope + +val Kosmos.keyguardServiceLockNowInteractor by + Kosmos.Fixture { KeyguardServiceLockNowInteractor(backgroundScope = testScope) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt index 63e168d018bf..4aa132c1d3af 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt @@ -41,5 +41,7 @@ val Kosmos.keyguardWakeDirectlyToGoneInteractor by lockPatternUtils, fakeSettings, selectedUserInteractor, + keyguardEnabledInteractor, + keyguardServiceLockNowInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 3d60abf59d62..5b2c8dda2475 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -78,6 +78,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.activeNotif import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.fakeAutoHideControllerStore import com.android.systemui.statusbar.phone.keyguardBypassController import com.android.systemui.statusbar.phone.scrimController import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository @@ -132,6 +133,7 @@ class KosmosJavaAdapter() { val simBouncerInteractor by lazy { kosmos.simBouncerInteractor } val statusBarStateController by lazy { kosmos.statusBarStateController } val statusBarModePerDisplayRepository by lazy { kosmos.fakeStatusBarModePerDisplayRepository } + val autoHideControllerStore by lazy { kosmos.fakeAutoHideControllerStore } val interactionJankMonitor by lazy { kosmos.interactionJankMonitor } val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig } val sceneInteractor by lazy { kosmos.sceneInteractor } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt index 562980d43485..06822a6c2339 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt @@ -19,10 +19,12 @@ package com.android.systemui.qs import com.android.internal.logging.InstanceId import com.android.systemui.animation.Expandable import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.plugins.qs.TileDetailsViewModel class FakeQSTile(var user: Int, var available: Boolean = true) : QSTile { private var tileSpec: String? = null var destroyed = false + var hasDetailsViewModel: Boolean = true private var state = QSTile.State() val callbacks = mutableListOf<QSTile.Callback>() @@ -91,6 +93,13 @@ class FakeQSTile(var user: Int, var available: Boolean = true) : QSTile { return false } + override fun getDetailsViewModel(): FakeTileDetailsViewModel? { + if (hasDetailsViewModel) { + return FakeTileDetailsViewModel(tileSpec) + } + return null + } + fun changeState(newState: QSTile.State) { state = newState callbacks.forEach { it.onStateChanged(state) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt new file mode 100644 index 000000000000..555f019822c2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import com.android.systemui.plugins.qs.TileDetailsViewModel + +class FakeTileDetailsViewModel(var tileSpec: String?) : TileDetailsViewModel() { + private var _clickOnSettingsButton = 0 + + @Composable + override fun GetContentView() { + Text( + text = "Fake details content", + textAlign = TextAlign.Center, + fontWeight = FontWeight.ExtraBold, + ) + } + + override fun clickOnSettingsButton() { + _clickOnSettingsButton++ + } + + override fun getTitle(): String { + return tileSpec ?: " Fake title" + } + + override fun getSubTitle(): String { + return tileSpec ?: "Fake sub title" + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt index b5a6bf126719..6fe860cfd0d3 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt @@ -19,12 +19,14 @@ package com.android.systemui.qs.panels.domain.interactor import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider import com.android.systemui.kosmos.Kosmos import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout +import com.android.systemui.qs.panels.ui.viewmodel.detailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridViewModelFactory val Kosmos.infiniteGridLayout by Kosmos.Fixture { InfiniteGridLayout( + detailsViewModel, iconTilesViewModel, infiniteGridViewModelFactory, tileHapticsViewModelFactoryProvider, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt new file mode 100644 index 000000000000..dc22905ba320 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor + +val Kosmos.detailsViewModel by Kosmos.Fixture { DetailsViewModel(currentTilesInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt index a90876551d20..de9f629ef787 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles.viewmodel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.util.mockito.mock val Kosmos.qsTileViewModelAdaperFactory by @@ -28,6 +29,7 @@ val Kosmos.qsTileViewModelAdaperFactory by applicationCoroutineScope, mock(), qsTileViewModel, + testDispatcher, ) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt index ce103ec57a86..6afc0d803f8d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.panels.ui.viewmodel.detailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel import com.android.systemui.qs.panels.ui.viewmodel.quickQuickSettingsViewModelFactory import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel @@ -34,6 +35,7 @@ val Kosmos.quickSettingsContainerViewModelFactory by supportsBrightnessMirroring, tileGridViewModel, editModeViewModel, + detailsViewModel, ) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt new file mode 100644 index 000000000000..dbaa0b1d5bf6 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.data.repository + +import android.view.Display +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.shade.display.ShadeDisplayPolicy +import com.android.systemui.shade.display.SpecificDisplayIdPolicy + +val Kosmos.defaultShadeDisplayPolicy: ShadeDisplayPolicy by + Kosmos.Fixture { SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY) } + +val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by + Kosmos.Fixture { + ShadeDisplaysRepositoryImpl( + defaultPolicy = defaultShadeDisplayPolicy, + bgScope = testScope.backgroundScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt new file mode 100644 index 000000000000..1c095e11dffa --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.domain.interactor + +import com.android.systemui.activity.data.repository.activityManagerRepository +import com.android.systemui.activity.data.repository.fake +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.chips.statusBarChipsLogger + +val Kosmos.singleNotificationChipInteractorFactory: SingleNotificationChipInteractor.Factory by + Kosmos.Fixture { + SingleNotificationChipInteractor.Factory { startingModel -> + SingleNotificationChipInteractor( + startingModel, + activityManagerRepository.fake, + logBuffer = statusBarChipsLogger, + ) + } + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt index 74c7611a6392..03e9f3d52ca3 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt @@ -17,6 +17,16 @@ package com.android.systemui.statusbar.chips.notification.domain.interactor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.chips.statusBarChipsLogger +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor val Kosmos.statusBarNotificationChipsInteractor: StatusBarNotificationChipsInteractor by - Kosmos.Fixture { StatusBarNotificationChipsInteractor() } + Kosmos.Fixture { + StatusBarNotificationChipsInteractor( + testScope.backgroundScope, + activeNotificationsInteractor, + singleNotificationChipInteractorFactory, + logBuffer = statusBarChipsLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt index 68b28adb4b3a..4bcce8601d64 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt @@ -19,13 +19,8 @@ package com.android.systemui.statusbar.chips.notification.ui.viewmodel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor -import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor val Kosmos.notifChipsViewModel: NotifChipsViewModel by Kosmos.Fixture { - NotifChipsViewModel( - applicationCoroutineScope, - activeNotificationsInteractor, - statusBarNotificationChipsInteractor, - ) + NotifChipsViewModel(applicationCoroutineScope, statusBarNotificationChipsInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt index 9197dcdc3f68..2d88ae8e926a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.core import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository +import com.android.systemui.statusbar.phone.AutoHideController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository import kotlinx.coroutines.CoroutineScope @@ -36,6 +37,7 @@ class FakeStatusBarOrchestratorFactory : StatusBarOrchestrator.Factory { statusBarModeRepository: StatusBarModePerDisplayRepository, statusBarInitializer: StatusBarInitializer, statusBarWindowController: StatusBarWindowController, + autoHideController: AutoHideController, ): StatusBarOrchestrator = mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt index 28edae7c3689..8c37bd739bc5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt @@ -34,6 +34,7 @@ import com.android.systemui.statusbar.data.repository.privacyDotWindowController import com.android.systemui.statusbar.data.repository.statusBarModeRepository import com.android.systemui.statusbar.mockNotificationRemoteInputManager import com.android.systemui.statusbar.phone.mockAutoHideController +import com.android.systemui.statusbar.phone.multiDisplayAutoHideControllerStore import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore import com.android.systemui.statusbar.window.fakeStatusBarWindowController @@ -50,9 +51,9 @@ val Kosmos.statusBarOrchestrator by fakeStatusBarInitializer, fakeStatusBarWindowController, applicationCoroutineScope.coroutineContext, + mockAutoHideController, mockDemoModeController, mockPluginDependencyProvider, - mockAutoHideController, mockNotificationRemoteInputManager, { mockNotificationShadeWindowViewController }, mockShadeSurface, @@ -80,6 +81,7 @@ val Kosmos.multiDisplayStatusBarStarter by statusBarInitializerStore, statusBarWindowControllerStore, statusBarInitializerStore, + multiDisplayAutoHideControllerStore, privacyDotWindowControllerStore, lightBarControllerStore, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt index 980d65fde6de..7de22d8c8b43 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt @@ -16,13 +16,27 @@ package com.android.systemui.statusbar.notification.data.repository +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import kotlinx.coroutines.flow.MutableStateFlow class FakeHeadsUpRowRepository(override val key: String, override val elementKey: Any = Any()) : HeadsUpRowRepository { - constructor(key: String, isPinned: Boolean) : this(key = key) { - this.isPinned.value = isPinned + constructor( + key: String, + elementKey: Any = Any(), + isPinned: Boolean, + ) : this(key = key, elementKey = elementKey) { + this.pinnedStatus.value = if (isPinned) PinnedStatus.PinnedBySystem else PinnedStatus.NotPinned } - override val isPinned: MutableStateFlow<Boolean> = MutableStateFlow(false) + constructor( + key: String, + elementKey: Any = Any(), + pinnedStatus: PinnedStatus, + ) : this(key = key, elementKey = elementKey) { + this.pinnedStatus.value = pinnedStatus + } + + override val pinnedStatus: MutableStateFlow<PinnedStatus> = + MutableStateFlow(PinnedStatus.NotPinned) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/HeadsUpManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt index a4db00c9b6ae..de9485d48383 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/HeadsUpManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt index d9d4361411bb..88caf6e2ba30 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt @@ -14,18 +14,14 @@ * limitations under the License. */ -package com.android.server.wm.flicker.testapp; +package com.android.systemui.statusbar.notification.promoted -import android.content.Intent; -import android.os.Bundle; +import com.android.systemui.statusbar.notification.collection.NotificationEntry -public class BottomHalfPipLaunchingActivity extends SimpleActivity { +class FakePromotedNotificationsProvider : PromotedNotificationsProvider { + val promotedEntries = mutableSetOf<NotificationEntry>() - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - final Intent intent = new Intent(this, BottomHalfPipActivity.class); - startActivity(intent); + override fun shouldPromote(entry: NotificationEntry): Boolean { + return promotedEntries.contains(entry) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt new file mode 100644 index 000000000000..5e9f12b4b1cc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +var Kosmos.promotedNotificationContentExtractor by + Kosmos.Fixture { + PromotedNotificationContentExtractor( + promotedNotificationsProvider, + applicationContext, + promotedNotificationLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt new file mode 100644 index 000000000000..2805d1e02356 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.log.logcatLogBuffer + +val Kosmos.promotedNotificationLogger by + Kosmos.Fixture { PromotedNotificationLogger(logcatLogBuffer("PromotedNotifLog")) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt index a7aa0b41a7aa..580f617a3cdf 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt @@ -18,4 +18,5 @@ package com.android.systemui.statusbar.notification.promoted import com.android.systemui.kosmos.Kosmos -val Kosmos.promotedNotificationsProvider by Kosmos.Fixture { PromotedNotificationsProviderImpl() } +var Kosmos.promotedNotificationsProvider: PromotedNotificationsProvider by + Kosmos.Fixture { PromotedNotificationsProviderImpl() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index 31d390862540..2d3f68faf4b7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -36,6 +36,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.util.MediaFeatureFlag import com.android.systemui.media.dialog.MediaOutputDialogManager import com.android.systemui.plugins.ActivityStarter @@ -60,9 +61,13 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.icon.IconBuilder import com.android.systemui.statusbar.notification.icon.IconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProviderImpl import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.CoordinateOnClickListener import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener @@ -77,7 +82,6 @@ import com.android.systemui.statusbar.notification.row.shared.NotificationRowCon import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardDismissUtil -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.SmartActionInflaterImpl import com.android.systemui.statusbar.policy.SmartReplyConstants import com.android.systemui.statusbar.policy.SmartReplyInflaterImpl @@ -221,6 +225,17 @@ class ExpandableNotificationRowBuilder( Mockito.mock(ConversationNotificationManager::class.java, STUB_ONLY), ) + val promotedNotificationsProvider = PromotedNotificationsProviderImpl() + val promotedNotificationLog = logcatLogBuffer("PromotedNotifLog") + val promotedNotificationLogger = PromotedNotificationLogger(promotedNotificationLog) + + val promotedNotificationContentExtractor = + PromotedNotificationContentExtractor( + promotedNotificationsProvider, + context, + promotedNotificationLogger, + ) + mContentBinder = if (NotificationRowContentBinderRefactor.isEnabled) NotificationRowContentBinderImpl( @@ -231,6 +246,7 @@ class ExpandableNotificationRowBuilder( smartReplyStateInflater, notifLayoutInflaterFactoryProvider, Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY), + promotedNotificationContentExtractor, Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY), ) else @@ -243,6 +259,7 @@ class ExpandableNotificationRowBuilder( smartReplyStateInflater, notifLayoutInflaterFactoryProvider, Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY), + promotedNotificationContentExtractor, Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY), ) mContentBinder.setInflateSynchronously(true) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt index 1e3897ba46c6..ec54c33f1156 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt @@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.visual import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl import com.android.systemui.statusbar.notification.collection.notifCollection import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider -import com.android.systemui.statusbar.policy.headsUpManager +import com.android.systemui.statusbar.notification.headsup.headsUpManager var Kosmos.onUserInteractionCallback: OnUserInteractionCallback by Kosmos.Fixture { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt index e20ce270fc14..a5c4bfd61945 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.stack import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.statusbar.policy.AvalancheController +import com.android.systemui.statusbar.notification.headsup.AvalancheController import com.android.systemui.util.mockito.mock var Kosmos.stackScrollAlgorithmSectionProvider by Fixture { @@ -29,6 +29,4 @@ var Kosmos.stackScrollAlgorithmBypassController by Fixture { mock<StackScrollAlgorithm.BypassController>() } -var Kosmos.avalancheController by Fixture { - mock<AvalancheController>() -} +var Kosmos.avalancheController by Fixture { mock<AvalancheController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt index 7fbf4e495feb..d1619b7959f2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt @@ -23,6 +23,7 @@ import com.android.systemui.dump.dumpManager import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToGoneTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel import com.android.systemui.keyguard.ui.viewmodel.aodToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel @@ -71,6 +72,8 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { shadeInteractor = shadeInteractor, notificationStackAppearanceInteractor = notificationStackAppearanceInteractor, alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel, + alternateBouncerToPrimaryBouncerTransitionViewModel = + alternateBouncerToPrimaryBouncerTransitionViewModel, aodToGoneTransitionViewModel = aodToGoneTransitionViewModel, aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt index 9c3f510c8585..e972c2c77902 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt @@ -25,7 +25,7 @@ import com.android.systemui.scene.data.repository.windowRootViewVisibilityReposi import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor -import com.android.systemui.statusbar.policy.headsUpManager +import com.android.systemui.statusbar.notification.headsup.headsUpManager val Kosmos.windowRootViewVisibilityInteractor by Fixture { WindowRootViewVisibilityInteractor( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt index 951ae59ebcf1..b99e93abfa30 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone import android.content.Context import android.os.Handler +import android.view.Display import android.view.IWindowManager import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository @@ -28,8 +29,6 @@ import org.mockito.Mockito.mock val Kosmos.mockAutoHideController: AutoHideController by Kosmos.Fixture { mock(AutoHideController::class.java) } -var Kosmos.autoHideController by Kosmos.Fixture { mockAutoHideController } - val Kosmos.fakeAutoHideControllerFactory by Kosmos.Fixture { FakeAutoHideControllerFactory() } val Kosmos.multiDisplayAutoHideControllerStore by @@ -42,6 +41,8 @@ val Kosmos.multiDisplayAutoHideControllerStore by ) } +val Kosmos.fakeAutoHideControllerStore by Kosmos.Fixture { FakeAutoHideControllerStore() } + class FakeAutoHideControllerFactory : AutoHideControllerImpl.Factory(mock(Handler::class.java), mock(IWindowManager::class.java)) { @@ -49,3 +50,15 @@ class FakeAutoHideControllerFactory : return mock(AutoHideControllerImpl::class.java) } } + +class FakeAutoHideControllerStore : AutoHideControllerStore { + + private val perDisplayMocks = mutableMapOf<Int, AutoHideController>() + + override val defaultDisplay: AutoHideController + get() = forDisplay(Display.DEFAULT_DISPLAY) + + override fun forDisplay(displayId: Int): AutoHideController { + return perDisplayMocks.computeIfAbsent(displayId) { mock(AutoHideController::class.java) } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt index 4a6757db9481..f86824a1af6d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt @@ -26,10 +26,10 @@ import com.android.systemui.keyguard.wakefulnessLifecycle import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notificationShadeWindowController import com.android.systemui.statusbar.policy.batteryController import com.android.systemui.statusbar.policy.deviceProvisionedController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.statusbar.pulseExpansionHandler import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt index 87ea1473bba1..5b7f23b0cff2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt @@ -24,16 +24,15 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notificationShadeWindowController import com.android.systemui.statusbar.policy.configurationController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.util.mockito.mock -import org.mockito.Mockito.mock -var Kosmos.statusBarTouchableRegionManager by +var Kosmos.shadeTouchableRegionManager by Kosmos.Fixture { - StatusBarTouchableRegionManager( + ShadeTouchableRegionManager( applicationContext, notificationShadeWindowController, configurationController, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt index c6684af9d5eb..6083414240a6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt @@ -36,6 +36,7 @@ import com.android.systemui.shade.shadeController import com.android.systemui.statusbar.commandQueue import com.android.systemui.statusbar.notification.collection.provider.launchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notification.notificationTransitionAnimatorControllerProvider import com.android.systemui.statusbar.notification.row.onUserInteractionCallback import com.android.systemui.statusbar.notificationClickNotifier @@ -43,7 +44,6 @@ import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.statusbar.notificationPresenter import com.android.systemui.statusbar.notificationRemoteInputManager import com.android.systemui.statusbar.notificationShadeWindowController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.statusbar.policy.keyguardStateController import com.android.systemui.wmshell.bubblesManager import java.util.Optional diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt index 03e4c894c2f2..eb17237afbb5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt @@ -26,6 +26,7 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel import com.android.systemui.statusbar.events.domain.interactor.systemStatusEventAnimationInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor @@ -35,6 +36,7 @@ val Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by collapsedStatusBarInteractor, lightsOutInteractor, activeNotificationsInteractor, + headsUpNotificationInteractor, keyguardTransitionInteractor, keyguardInteractor, sceneInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt new file mode 100644 index 000000000000..f12089a08488 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.touchpad.ui.gesture + +import android.view.MotionEvent +import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity +import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker + +class FakeVelocityTracker : VelocityTracker { + + private var fakeVelocity = Velocity(0f) + + override fun calculateVelocity(): Velocity { + return fakeVelocity + } + + override fun accept(event: MotionEvent) {} + + fun setVelocity(velocity: Velocity) { + fakeVelocity = velocity + } +} diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/VelocityTrackerKosmos.kt index 7d3d8b9f7974..3b61e2191d8c 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/VelocityTrackerKosmos.kt @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.platform.test.ravenwood; -/** Stub class. The actual implementation is in junit-impl-src. */ -public class RavenwoodConfigState { - public RavenwoodConfigState(RavenwoodConfig config) { - } -} +package com.android.systemui.touchpad.ui.gesture + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.fakeVelocityTracker: FakeVelocityTracker by Kosmos.Fixture { FakeVelocityTracker() } diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp index a462297c07af..03ef4e67af1c 100644 --- a/packages/Vcn/service-b/Android.bp +++ b/packages/Vcn/service-b/Android.bp @@ -19,6 +19,19 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +filegroup { + name: "vcn-location-sources", + srcs: select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), { + true: [ + "vcn-location-flag/module/com/android/server/vcn/VcnLocation.java", + ], + default: [ + "vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java", + ], + }), + visibility: ["//frameworks/base/services/core"], +} + java_library { name: "service-connectivity-b-pre-jarjar", sdk_version: "system_server_current", diff --git a/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java new file mode 100644 index 000000000000..6c7d24db8bac --- /dev/null +++ b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +/** + * Class to represent that VCN is in a mainline module + * + * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline + * module. + */ +// When VCN is in a mainline module, this class (module/com/android/server/vcn/VcnLocation.java) +// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable +// platform, platform/com/android/server/vcn/VcnLocation.java will be built in to the filegroup +public class VcnLocation { + /** Indicate that VCN is the platform */ + public static final boolean IS_VCN_IN_MAINLINE = true; +} diff --git a/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java new file mode 100644 index 000000000000..c6c82a55de0f --- /dev/null +++ b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +/** + * Class to represent that VCN is in the platform + * + * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline + * module. + */ +// When VCN is in a mainline module, module/com/android/server/vcn/VcnLocation.java +// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable +// platform, this class (platform/com/android/server/vcn/VcnLocation.java) will be built in to the +// filegroup +public class VcnLocation { + /** Indicate that VCN is the platform */ + public static final boolean IS_VCN_IN_MAINLINE = false; +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java index 9b71f8050c80..de3c5f2c23ab 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java @@ -133,9 +133,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName()); - // This is needed to make AndroidJUnit4ClassRunner happy. - InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); - // Hook point to allow more customization. runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null); diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java deleted file mode 100644 index 870a10a1f57e..000000000000 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.platform.test.ravenwood; - -import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.annotation.Nullable; -import android.app.ResourcesManager; -import android.content.res.Resources; -import android.view.DisplayAdjustments; - -import java.io.File; -import java.util.HashMap; - -/** - * Used to store various states associated with {@link RavenwoodConfig} that's inly needed - * in junit-impl. - * - * We don't want to put it in junit-src to avoid having to recompile all the downstream - * dependencies after changing this class. - * - * All members must be called from the runner's main thread. - */ -public class RavenwoodConfigState { - private static final String TAG = "RavenwoodConfigState"; - - private final RavenwoodConfig mConfig; - - // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by - // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry? - RavenwoodContext mSystemServerContext; - - public RavenwoodConfigState(RavenwoodConfig config) { - mConfig = config; - } - - /** Map from path -> resources. */ - private final HashMap<File, Resources> mCachedResources = new HashMap<>(); - - /** - * Load {@link Resources} from an APK, with cache. - */ - public Resources loadResources(@Nullable File apkPath) { - var cached = mCachedResources.get(apkPath); - if (cached != null) { - return cached; - } - - var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK); - - assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile()); - - final String path = fileToLoad.getAbsolutePath(); - final var emptyPaths = new String[0]; - - ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths); - - final var ret = ResourcesManager.getInstance().getResources(null, path, - emptyPaths, emptyPaths, emptyPaths, - emptyPaths, null, null, - new DisplayAdjustments().getCompatibilityInfo(), - RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null); - - assertNotNull(ret); - - mCachedResources.put(apkPath, ret); - return ret; - } -} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java index ec00e8fea887..6dfcf4ce03cf 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java @@ -15,24 +15,23 @@ */ package android.platform.test.ravenwood; -import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicMember; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.annotation.Nullable; import android.util.Log; +import android.util.Pair; -import com.android.ravenwood.common.RavenwoodRuntimeException; +import com.android.ravenwood.RavenwoodRuntimeNative; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.rules.TestRule; import org.junit.runner.Description; -import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; /** - * Used to store various states associated with the current test runner that's inly needed + * Used to store various states associated with the current test runner that's only needed * in junit-impl. * * We don't want to put it in junit-src to avoid having to recompile all the downstream @@ -42,6 +41,11 @@ import java.lang.reflect.Field; */ public final class RavenwoodRunnerState { private static final String TAG = "RavenwoodRunnerState"; + private static final String RAVENWOOD_RULE_ERROR = + "RavenwoodRule(s) are not executed in the correct order"; + + private static final List<Pair<RavenwoodRule, RavenwoodPropertyState>> sActiveProperties = + new ArrayList<>(); private final RavenwoodAwareTestRunner mRunner; @@ -52,207 +56,95 @@ public final class RavenwoodRunnerState { mRunner = runner; } - /** - * The RavenwoodConfig used to configure the current Ravenwood environment. - * This can either come from mConfig or mRule. - */ - private RavenwoodConfig mCurrentConfig; - /** - * The RavenwoodConfig declared in the test class - */ - private RavenwoodConfig mConfig; - /** - * The RavenwoodRule currently in effect, declared in the test class - */ - private RavenwoodRule mRule; - private boolean mHasRavenwoodRule; private Description mMethodDescription; - public RavenwoodConfig getConfig() { - return mCurrentConfig; - } - public void enterTestRunner() { Log.i(TAG, "enterTestRunner: " + mRunner); - - mHasRavenwoodRule = hasRavenwoodRule(mRunner.mTestJavaClass); - mConfig = extractConfiguration(mRunner.mTestJavaClass); - - if (mConfig != null) { - if (mHasRavenwoodRule) { - fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class." - + " Suggest migrating to RavenwoodConfig."); - } - mCurrentConfig = mConfig; - } else if (!mHasRavenwoodRule) { - // If no RavenwoodConfig and no RavenwoodRule, use a default config - mCurrentConfig = new RavenwoodConfig.Builder().build(); - } - - if (mCurrentConfig != null) { - RavenwoodRuntimeEnvironmentController.init(mRunner); - } + RavenwoodRuntimeEnvironmentController.initForRunner(); } public void enterTestClass() { Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName()); - - if (mCurrentConfig != null) { - RavenwoodRuntimeEnvironmentController.init(mRunner); - } } public void exitTestClass() { Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName()); - try { - if (mCurrentConfig != null) { - RavenwoodRuntimeEnvironmentController.reset(); - } - } finally { - mConfig = null; - mRule = null; - } + assertTrue(RAVENWOOD_RULE_ERROR, sActiveProperties.isEmpty()); + RavenwoodRuntimeEnvironmentController.exitTestClass(); } public void enterTestMethod(Description description) { mMethodDescription = description; + RavenwoodRuntimeEnvironmentController.initForMethod(); } public void exitTestMethod() { mMethodDescription = null; - RavenwoodRuntimeEnvironmentController.reinit(); } public void enterRavenwoodRule(RavenwoodRule rule) { - if (!mHasRavenwoodRule) { - fail("If you have a RavenwoodRule in your test, make sure the field type is" - + " RavenwoodRule so Ravenwood can detect it."); - } - if (mRule != null) { - fail("Multiple nesting RavenwoodRule's are detected in the same class," - + " which is not supported."); - } - mRule = rule; - if (mCurrentConfig == null) { - mCurrentConfig = rule.getConfiguration(); - } - RavenwoodRuntimeEnvironmentController.init(mRunner); + pushTestProperties(rule); } public void exitRavenwoodRule(RavenwoodRule rule) { - if (mRule != rule) { - fail("RavenwoodRule did not take effect."); - } - mRule = null; + popTestProperties(rule); } - /** - * @return a configuration from a test class, if any. - */ - @Nullable - private static RavenwoodConfig extractConfiguration(Class<?> testClass) { - var field = findConfigurationField(testClass); - if (field == null) { - return null; - } + static class RavenwoodPropertyState { - try { - return (RavenwoodConfig) field.get(null); - } catch (IllegalAccessException e) { - throw new RavenwoodRuntimeException("Failed to fetch from the configuration field", e); - } - } + final List<Pair<String, String>> mBackup; + final Set<String> mKeyReadable; + final Set<String> mKeyWritable; - /** - * @return true if the current target class (or its super classes) has any @Rule / @ClassRule - * fields of type RavenwoodRule. - * - * Note, this check won't detect cases where a Rule is of type - * {@link TestRule} and still be a {@link RavenwoodRule}. But that'll be detected at runtime - * as a failure, in {@link #enterRavenwoodRule}. - */ - private static boolean hasRavenwoodRule(Class<?> testClass) { - for (var field : testClass.getDeclaredFields()) { - if (!field.isAnnotationPresent(Rule.class) - && !field.isAnnotationPresent(ClassRule.class)) { - continue; - } - if (field.getType().equals(RavenwoodRule.class)) { - return true; - } + RavenwoodPropertyState(RavenwoodTestProperties props) { + mBackup = props.mValues.keySet().stream() + .map(key -> Pair.create(key, RavenwoodRuntimeNative.getSystemProperty(key))) + .toList(); + mKeyReadable = Set.copyOf(props.mKeyReadable); + mKeyWritable = Set.copyOf(props.mKeyWritable); } - // JUnit supports rules as methods, so we need to check them too. - for (var method : testClass.getDeclaredMethods()) { - if (!method.isAnnotationPresent(Rule.class) - && !method.isAnnotationPresent(ClassRule.class)) { - continue; - } - if (method.getReturnType().equals(RavenwoodRule.class)) { - return true; - } + + boolean isKeyAccessible(String key, boolean write) { + return write ? mKeyWritable.contains(key) : mKeyReadable.contains(key); } - // Look into the super class. - if (!testClass.getSuperclass().equals(Object.class)) { - return hasRavenwoodRule(testClass.getSuperclass()); + + void restore() { + mBackup.forEach(pair -> { + if (pair.second == null) { + RavenwoodRuntimeNative.removeSystemProperty(pair.first); + } else { + RavenwoodRuntimeNative.setSystemProperty(pair.first, pair.second); + } + }); } - return false; } - /** - * Find and return a field with @RavenwoodConfig.Config, which must be of type - * RavenwoodConfig. - */ - @Nullable - private static Field findConfigurationField(Class<?> testClass) { - Field foundField = null; - - for (var field : testClass.getDeclaredFields()) { - final var hasAnot = field.isAnnotationPresent(RavenwoodConfig.Config.class); - final var isType = field.getType().equals(RavenwoodConfig.class); + private static void pushTestProperties(RavenwoodRule rule) { + sActiveProperties.add(Pair.create(rule, new RavenwoodPropertyState(rule.mProperties))); + rule.mProperties.mValues.forEach(RavenwoodRuntimeNative::setSystemProperty); + } - if (hasAnot) { - if (isType) { - // Good, use this field. - if (foundField != null) { - fail(String.format( - "Class %s has multiple fields with %s", - testClass.getCanonicalName(), - "@RavenwoodConfig.Config")); - } - // Make sure it's static public - ensureIsPublicMember(field, true); + private static void popTestProperties(RavenwoodRule rule) { + var pair = sActiveProperties.removeLast(); + assertNotNull(RAVENWOOD_RULE_ERROR, pair); + assertEquals(RAVENWOOD_RULE_ERROR, rule, pair.first); + pair.second.restore(); + } - foundField = field; - } else { - fail(String.format( - "Field %s.%s has %s but type is not %s", - testClass.getCanonicalName(), - field.getName(), - "@RavenwoodConfig.Config", - "RavenwoodConfig")); - return null; // unreachable - } - } else { - if (isType) { - fail(String.format( - "Field %s.%s does not have %s but type is %s", - testClass.getCanonicalName(), - field.getName(), - "@RavenwoodConfig.Config", - "RavenwoodConfig")); - return null; // unreachable - } else { - // Unrelated field, ignore. - continue; - } - } - } - if (foundField != null) { - return foundField; + @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp) + private static void checkSystemPropertyAccess(String key, boolean write) { + if (write && RavenwoodSystemProperties.sDefaultValues.containsKey(key)) { + // The default core values should never be modified + throw new IllegalArgumentException( + "Setting core system property '" + key + "' is not allowed"); } - if (!testClass.getSuperclass().equals(Object.class)) { - return findConfigurationField(testClass.getSuperclass()); + + final boolean result = RavenwoodSystemProperties.isKeyAccessible(key, write) + || sActiveProperties.stream().anyMatch(p -> p.second.isKeyAccessible(key, write)); + + if (!result) { + throw new IllegalArgumentException((write ? "Write" : "Read") + + " access to system property '" + key + "' denied via RavenwoodRule"); } - return null; } } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 979076eaabfd..e730a292a9da 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -16,8 +16,11 @@ package android.platform.test.ravenwood; +import static android.os.Process.FIRST_APPLICATION_UID; +import static android.os.UserHandle.SYSTEM; import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME; +import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; @@ -25,7 +28,9 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSIO import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt; import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -53,6 +58,7 @@ import android.provider.DeviceConfig_host; import android.system.ErrnoException; import android.system.Os; import android.util.Log; +import android.view.DisplayAdjustments; import androidx.test.platform.app.InstrumentationRegistry; @@ -62,7 +68,6 @@ import com.android.internal.os.RuntimeInit; import com.android.ravenwood.RavenwoodRuntimeNative; import com.android.ravenwood.RavenwoodRuntimeState; import com.android.ravenwood.common.RavenwoodCommonUtils; -import com.android.ravenwood.common.RavenwoodRuntimeException; import com.android.ravenwood.common.SneakyThrow; import com.android.server.LocalServices; import com.android.server.compat.PlatformCompat; @@ -74,8 +79,10 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -85,8 +92,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; /** - * Responsible for initializing and de-initializing the environment, according to a - * {@link RavenwoodConfig}. + * Responsible for initializing and the environment. */ public class RavenwoodRuntimeEnvironmentController { private static final String TAG = "RavenwoodRuntimeEnvironmentController"; @@ -113,8 +119,6 @@ public class RavenwoodRuntimeEnvironmentController { private static ScheduledFuture<?> sPendingTimeout; - private static long sOriginalIdentityToken = -1; - /** * When enabled, attempt to detect uncaught exceptions from background threads. */ @@ -147,6 +151,10 @@ public class RavenwoodRuntimeEnvironmentController { return res; } + /** Map from path -> resources. */ + private static final HashMap<File, Resources> sCachedResources = new HashMap<>(); + private static Set<String> sAdoptedPermissions = Collections.emptySet(); + private static final Object sInitializationLock = new Object(); @GuardedBy("sInitializationLock") @@ -155,15 +163,16 @@ public class RavenwoodRuntimeEnvironmentController { @GuardedBy("sInitializationLock") private static Throwable sExceptionFromGlobalInit; - private static RavenwoodAwareTestRunner sRunner; - private static RavenwoodSystemProperties sProps; - private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT; private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname"; + private static final int sMyPid = new Random().nextInt(100, 32768); private static int sTargetSdkLevel; private static String sTestPackageName; private static String sTargetPackageName; + private static Instrumentation sInstrumentation; + private static final long sCallingIdentity = + packBinderIdentityToken(false, FIRST_APPLICATION_UID, sMyPid); /** * Initialize the global environment. @@ -182,7 +191,7 @@ public class RavenwoodRuntimeEnvironmentController { Log.e(TAG, "globalInit() failed", th); sExceptionFromGlobalInit = th; - throw th; + SneakyThrow.sneakyThrow(th); } } else { // Subsequent calls. If the first call threw, just throw the same error, to prevent @@ -197,10 +206,13 @@ public class RavenwoodRuntimeEnvironmentController { } } - private static void globalInitInner() { + private static void globalInitInner() throws IOException { if (RAVENWOOD_VERBOSE_LOGGING) { Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH")); } + if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { + Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler); + } // Some process-wide initialization. (maybe redirect stdout/stderr) RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME); @@ -220,7 +232,6 @@ public class RavenwoodRuntimeEnvironmentController { // Do the basic set up for the android sysprops. RavenwoodSystemProperties.initialize(); - setSystemProperties(null); // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()), // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS). @@ -251,82 +262,32 @@ public class RavenwoodRuntimeEnvironmentController { loadRavenwoodProperties(); assertMockitoVersion(); - } - - private static void loadRavenwoodProperties() { - var props = RavenwoodSystemProperties.readProperties("ravenwood.properties"); - - sTargetSdkLevel = withDefault( - parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL); - sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME); - sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName); - - // TODO(b/377765941) Read them from the manifest too? - } - - /** - * Initialize the environment. - */ - public static void init(RavenwoodAwareTestRunner runner) { - if (RAVENWOOD_VERBOSE_LOGGING) { - Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE")); - } - if (sRunner == runner) { - return; - } - if (sRunner != null) { - reset(); - } - sRunner = runner; - try { - initInner(runner.mState.getConfig()); - } catch (Exception th) { - Log.e(TAG, "init() failed", th); - - RavenwoodCommonUtils.runIgnoringException(()-> reset()); - - SneakyThrow.sneakyThrow(th); - } - } - - private static void initInner(RavenwoodConfig config) throws IOException { - if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { - maybeThrowPendingUncaughtException(false); - Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler); - } - - config.mTargetPackageName = sTargetPackageName; - config.mTestPackageName = sTestPackageName; - config.mTargetSdkLevel = sTargetSdkLevel; Log.i(TAG, "TargetPackageName=" + sTargetPackageName); Log.i(TAG, "TestPackageName=" + sTestPackageName); Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel); - RavenwoodRuntimeState.sUid = config.mUid; - RavenwoodRuntimeState.sPid = config.mPid; - RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel; - sOriginalIdentityToken = Binder.clearCallingIdentity(); - reinit(); - setSystemProperties(config.mSystemProperties); + RavenwoodRuntimeState.sUid = FIRST_APPLICATION_UID; + RavenwoodRuntimeState.sPid = sMyPid; + RavenwoodRuntimeState.sTargetSdkLevel = sTargetSdkLevel; ServiceManager.init$ravenwood(); LocalServices.removeAllServicesForTest(); - ActivityManager.init$ravenwood(config.mCurrentUser); + ActivityManager.init$ravenwood(SYSTEM.getIdentifier()); final var main = new HandlerThread(MAIN_THREAD_NAME); main.start(); Looper.setMainLooperForTest(main.getLooper()); final boolean isSelfInstrumenting = - Objects.equals(config.mTestPackageName, config.mTargetPackageName); + Objects.equals(sTestPackageName, sTargetPackageName); // This will load the resources from the apk set to `resource_apk` in the build file. // This is supposed to be the "target app"'s resources. final Supplier<Resources> targetResourcesLoader = () -> { var file = new File(RAVENWOOD_RESOURCE_APK); - return config.mState.loadResources(file.exists() ? file : null); + return loadResources(file.exists() ? file : null); }; // Set up test context's (== instrumentation context's) resources. @@ -337,18 +298,17 @@ public class RavenwoodRuntimeEnvironmentController { } else { instResourcesLoader = () -> { var file = new File(RAVENWOOD_INST_RESOURCE_APK); - return config.mState.loadResources(file.exists() ? file : null); + return loadResources(file.exists() ? file : null); }; } var instContext = new RavenwoodContext( - config.mTestPackageName, main, instResourcesLoader); + sTestPackageName, main, instResourcesLoader); var targetContext = new RavenwoodContext( - config.mTargetPackageName, main, targetResourcesLoader); + sTargetPackageName, main, targetResourcesLoader); // Set up app context. - var appContext = new RavenwoodContext( - config.mTargetPackageName, main, targetResourcesLoader); + var appContext = new RavenwoodContext(sTargetPackageName, main, targetResourcesLoader); appContext.setApplicationContext(appContext); if (isSelfInstrumenting) { instContext.setApplicationContext(appContext); @@ -357,42 +317,68 @@ public class RavenwoodRuntimeEnvironmentController { // When instrumenting into another APK, the test context doesn't have an app context. targetContext.setApplicationContext(appContext); } - config.mInstContext = instContext; - config.mTargetContext = targetContext; - final Supplier<Resources> systemResourcesLoader = () -> config.mState.loadResources(null); + final Supplier<Resources> systemResourcesLoader = () -> loadResources(null); - config.mState.mSystemServerContext = + var systemServerContext = new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader); - // Prepare other fields. - config.mInstrumentation = new Instrumentation(); - config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation()); - InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY); + sInstrumentation = new Instrumentation(); + sInstrumentation.basicInit(instContext, targetContext, null); + InstrumentationRegistry.registerInstance(sInstrumentation, Bundle.EMPTY); + + RavenwoodSystemServer.init(systemServerContext); + + initializeCompatIds(); + } + + private static void loadRavenwoodProperties() { + var props = RavenwoodSystemProperties.readProperties("ravenwood.properties"); + + sTargetSdkLevel = withDefault( + parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL); + sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME); + sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName); + + // TODO(b/377765941) Read them from the manifest too? + } - RavenwoodSystemServer.init(config); + /** + * Partially reset and initialize before each test class invocation + */ + public static void initForRunner() { + var targetContext = sInstrumentation.getTargetContext(); + var instContext = sInstrumentation.getContext(); + // We need to recreate the mock UiAutomation for each test class, because sometimes tests + // will call Mockito.framework().clearInlineMocks() after execution. + sInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation()); + + // Reset some global state + Process_ravenwood.reset(); + DeviceConfig_host.reset(); + Binder.restoreCallingIdentity(sCallingIdentity); - initializeCompatIds(config); + SystemProperties.clearChangeCallbacksForTest(); if (ENABLE_TIMEOUT_STACKS) { sPendingTimeout = sTimeoutExecutor.schedule( RavenwoodRuntimeEnvironmentController::dumpStacks, TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } + if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { + maybeThrowPendingUncaughtException(false); + } } /** - * Partially re-initialize after each test method invocation + * Partially reset and initialize before each test method invocation */ - public static void reinit() { - // sRunner could be null, if there was a failure in the initialization. - if (sRunner != null) { - var config = sRunner.mState.getConfig(); - Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid)); - } + public static void initForMethod() { + // TODO(b/375272444): this is a hacky workaround to ensure binder identity + Binder.restoreCallingIdentity(sCallingIdentity); } - private static void initializeCompatIds(RavenwoodConfig config) { + private static void initializeCompatIds() { // Set up compat-IDs for the app side. // TODO: Inside the system server, all the compat-IDs should be enabled, // Due to the `AppCompatCallbacks.install(new long[0], new long[0])` call in @@ -400,8 +386,8 @@ public class RavenwoodRuntimeEnvironmentController { // Compat framework only uses the package name and the target SDK level. ApplicationInfo appInfo = new ApplicationInfo(); - appInfo.packageName = config.mTargetPackageName; - appInfo.targetSdkVersion = config.mTargetSdkLevel; + appInfo.packageName = sTargetPackageName; + appInfo.targetSdkVersion = sTargetSdkLevel; PlatformCompat platformCompat = null; try { @@ -418,65 +404,42 @@ public class RavenwoodRuntimeEnvironmentController { } /** - * De-initialize. - * - * Note, we call this method when init() fails too, so this method should deal with - * any partially-initialized states. + * Load {@link Resources} from an APK, with cache. */ - public static void reset() { - if (RAVENWOOD_VERBOSE_LOGGING) { - Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE")); - } - if (sRunner == null) { - throw new RavenwoodRuntimeException("Internal error: reset() already called"); + private static Resources loadResources(@Nullable File apkPath) { + var cached = sCachedResources.get(apkPath); + if (cached != null) { + return cached; } - var config = sRunner.mState.getConfig(); - sRunner = null; - if (ENABLE_TIMEOUT_STACKS) { - sPendingTimeout.cancel(false); - } + var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK); - RavenwoodSystemServer.reset(config); + assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile()); - InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); - config.mInstrumentation = null; - if (config.mInstContext != null) { - ((RavenwoodContext) config.mInstContext).cleanUp(); - config.mInstContext = null; - } - if (config.mTargetContext != null) { - ((RavenwoodContext) config.mTargetContext).cleanUp(); - config.mTargetContext = null; - } - if (config.mState.mSystemServerContext != null) { - config.mState.mSystemServerContext.cleanUp(); - } + final String path = fileToLoad.getAbsolutePath(); + final var emptyPaths = new String[0]; - if (Looper.getMainLooper() != null) { - Looper.getMainLooper().quit(); - } - Looper.clearMainLooperForTest(); + ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths); - ActivityManager.reset$ravenwood(); + final var ret = ResourcesManager.getInstance().getResources(null, path, + emptyPaths, emptyPaths, emptyPaths, + emptyPaths, null, null, + new DisplayAdjustments().getCompatibilityInfo(), + RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null); - LocalServices.removeAllServicesForTest(); - ServiceManager.reset$ravenwood(); + assertNotNull(ret); - setSystemProperties(null); - if (sOriginalIdentityToken != -1) { - Binder.restoreCallingIdentity(sOriginalIdentityToken); - } - RavenwoodRuntimeState.reset(); - Process_ravenwood.reset(); - DeviceConfig_host.reset(); + sCachedResources.put(apkPath, ret); + return ret; + } - try { - ResourcesManager.setInstance(null); // Better structure needed. - } catch (Exception e) { - // AOSP-CHANGE: AOSP doesn't support resources yet. + /** + * A callback when a test class finishes its execution, mostly only for debugging. + */ + public static void exitTestClass() { + if (ENABLE_TIMEOUT_STACKS) { + sPendingTimeout.cancel(false); } - if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { maybeThrowPendingUncaughtException(true); } @@ -521,19 +484,6 @@ public class RavenwoodRuntimeEnvironmentController { } } - /** - * Set the current configuration to the actual SystemProperties. - */ - private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) { - SystemProperties.clearChangeCallbacksForTest(); - RavenwoodRuntimeNative.clearSystemProperties(); - if (systemProperties == null) systemProperties = new RavenwoodSystemProperties(); - sProps = new RavenwoodSystemProperties(systemProperties, true); - for (var entry : systemProperties.getValues().entrySet()) { - RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue()); - } - } - private static final String MOCKITO_ERROR = "FATAL: Unsupported Mockito detected!" + " Your test or its dependencies use one of the \"mockito-target-*\"" + " modules as static library, which is unusable on host side." @@ -558,40 +508,31 @@ public class RavenwoodRuntimeEnvironmentController { // TODO: use the real UiAutomation class instead of a mock private static UiAutomation createMockUiAutomation() { - final Set[] adoptedPermission = { Collections.emptySet() }; + sAdoptedPermissions = Collections.emptySet(); var mock = mock(UiAutomation.class, inv -> { HostTestUtils.onThrowMethodCalled(); return null; }); doAnswer(inv -> { - adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS; + sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; return null; }).when(mock).adoptShellPermissionIdentity(); doAnswer(inv -> { if (inv.getArgument(0) == null) { - adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS; + sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } else { - adoptedPermission[0] = Set.of(inv.getArguments()); + sAdoptedPermissions = (Set) Set.of(inv.getArguments()); } return null; }).when(mock).adoptShellPermissionIdentity(any()); doAnswer(inv -> { - adoptedPermission[0] = Collections.emptySet(); + sAdoptedPermissions = Collections.emptySet(); return null; }).when(mock).dropShellPermissionIdentity(); - doAnswer(inv -> adoptedPermission[0]).when(mock).getAdoptedShellPermissions(); + doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions(); return mock; } - @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp) - private static void checkSystemPropertyAccess(String key, boolean write) { - boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key); - if (!result) { - throw new IllegalArgumentException((write ? "Write" : "Read") - + " access to system property '" + key + "' denied via RavenwoodConfig"); - } - } - private static void dumpCommandLineArgs() { Log.i(TAG, "JVM arguments:"); diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java index 9bd376a76f77..c545baacdf3e 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package android.platform.test.ravenwood; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; @@ -21,26 +20,30 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRunt import android.util.Log; +import com.android.ravenwood.RavenwoodRuntimeNative; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +/** + * A class to manage the core default system properties of the Ravenwood environment. + */ public class RavenwoodSystemProperties { private static final String TAG = "RavenwoodSystemProperties"; - /** We pull in propeties from this file. */ + /** We pull in properties from this file. */ private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop"; /** This is the actual build.prop we use to build the device (contents depends on lunch). */ private static final String DEVICE_BUILD_PROP = "ravenwood-data/build.prop"; /** The default values. */ - private static final Map<String, String> sDefaultValues = new HashMap<>(); + static final Map<String, String> sDefaultValues = new HashMap<>(); private static final String[] PARTITIONS = { "bootimage", @@ -91,7 +94,7 @@ public class RavenwoodSystemProperties { var deviceValue = deviceProps.get(deviceKey); if (deviceValue == null) { throw new RuntimeException("Failed to initialize system properties. Key '" - + deviceKey + "' doesn't exist in the device side build.prop"); + + deviceKey + "' doesn't exist in the device side build.prop"); } value = deviceValue; } else { @@ -115,6 +118,7 @@ public class RavenwoodSystemProperties { } } } + if (RAVENWOOD_VERBOSE_LOGGING) { // Dump all properties for local debugging. Log.v(TAG, "All system properties:"); @@ -122,35 +126,12 @@ public class RavenwoodSystemProperties { Log.v(TAG, "" + key + "=" + sDefaultValues.get(key)); } } - } - - private volatile boolean mIsImmutable; - - private final Map<String, String> mValues = new HashMap<>(); - - /** Set of additional keys that should be considered readable */ - private final Set<String> mKeyReadable = new HashSet<>(); - - /** Set of additional keys that should be considered writable */ - private final Set<String> mKeyWritable = new HashSet<>(); - - public RavenwoodSystemProperties() { - mValues.putAll(sDefaultValues); - } - - /** Copy constructor */ - public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) { - mKeyReadable.addAll(source.mKeyReadable); - mKeyWritable.addAll(source.mKeyWritable); - mValues.putAll(source.mValues); - mIsImmutable = immutable; - } - public Map<String, String> getValues() { - return new HashMap<>(mValues); + // Actually set the system properties + sDefaultValues.forEach(RavenwoodRuntimeNative::setSystemProperty); } - public boolean isKeyReadable(String key) { + private static boolean isKeyReadable(String key) { final String root = getKeyRoot(key); if (root.startsWith("debug.")) return true; @@ -183,10 +164,10 @@ public class RavenwoodSystemProperties { return true; } - return mKeyReadable.contains(key); + return false; } - public boolean isKeyWritable(String key) { + private static boolean isKeyWritable(String key) { final String root = getKeyRoot(key); if (root.startsWith("debug.")) return true; @@ -194,42 +175,11 @@ public class RavenwoodSystemProperties { // For PropertyInvalidatedCache if (root.startsWith("cache_key.")) return true; - return mKeyWritable.contains(key); - } - - private void ensureNotImmutable() { - if (mIsImmutable) { - throw new RuntimeException("Unable to update immutable instance"); - } - } - - public void setValue(String key, Object value) { - ensureNotImmutable(); - - final String valueString = (value == null) ? null : String.valueOf(value); - if ((valueString == null) || valueString.isEmpty()) { - mValues.remove(key); - } else { - mValues.put(key, valueString); - } - } - - public void setAccessNone(String key) { - ensureNotImmutable(); - mKeyReadable.remove(key); - mKeyWritable.remove(key); - } - - public void setAccessReadOnly(String key) { - ensureNotImmutable(); - mKeyReadable.add(key); - mKeyWritable.remove(key); + return false; } - public void setAccessReadWrite(String key) { - ensureNotImmutable(); - mKeyReadable.add(key); - mKeyWritable.add(key); + static boolean isKeyAccessible(String key, boolean write) { + return write ? isKeyWritable(key) : isKeyReadable(key); } /** diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java index 438a2bfa7a14..3346635b7f7b 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java @@ -33,7 +33,7 @@ import com.android.server.compat.PlatformCompat; import com.android.server.compat.PlatformCompatNative; import com.android.server.utils.TimingsTraceAndSlog; -import java.util.List; +import java.util.Collection; import java.util.Set; public class RavenwoodSystemServer { @@ -68,27 +68,24 @@ public class RavenwoodSystemServer { private static TimingsTraceAndSlog sTimings; private static SystemServiceManager sServiceManager; - public static void init(RavenwoodConfig config) { + public static void init(Context systemServerContext) { // Always start PlatformCompat, regardless of the requested services. // PlatformCompat is not really a SystemService, so it won't receive boot phases / etc. // This initialization code is copied from SystemServer.java. - PlatformCompat platformCompat = new PlatformCompat(config.mState.mSystemServerContext); + PlatformCompat platformCompat = new PlatformCompat(systemServerContext); ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat); ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE, new PlatformCompatNative(platformCompat)); - // Avoid overhead if no services required - if (config.mServicesRequired.isEmpty()) return; - sStartedServices = new ArraySet<>(); sTimings = new TimingsTraceAndSlog(); - sServiceManager = new SystemServiceManager(config.mState.mSystemServerContext); + sServiceManager = new SystemServiceManager(systemServerContext); sServiceManager.setStartInfo(false, SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); LocalServices.addService(SystemServiceManager.class, sServiceManager); - startServices(config.mServicesRequired); + startServices(sKnownServices.keySet()); sServiceManager.sealStartedServices(); // TODO: expand to include additional boot phases when relevant @@ -96,7 +93,7 @@ public class RavenwoodSystemServer { sServiceManager.startBootPhase(sTimings, SystemService.PHASE_BOOT_COMPLETED); } - public static void reset(RavenwoodConfig config) { + public static void reset() { // TODO: consider introducing shutdown boot phases LocalServices.removeServiceForTest(SystemServiceManager.class); @@ -105,7 +102,7 @@ public class RavenwoodSystemServer { sStartedServices = null; } - private static void startServices(List<Class<?>> serviceClasses) { + private static void startServices(Collection<Class<?>> serviceClasses) { for (Class<?> serviceClass : serviceClasses) { // Quietly ignore duplicate requests if service already started if (sStartedServices.contains(serviceClass)) continue; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index 7ca9239d2062..3ed0f50434fb 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -15,21 +15,13 @@ */ package android.platform.test.ravenwood; -import static android.os.Process.FIRST_APPLICATION_UID; -import static android.os.UserHandle.SYSTEM; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Instrumentation; -import android.content.Context; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; /** * @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it. @@ -45,37 +37,10 @@ public final class RavenwoodConfig { public @interface Config { } - private static final int NOBODY_UID = 9999; - - private static final AtomicInteger sNextPid = new AtomicInteger(100); - - int mCurrentUser = SYSTEM.getIdentifier(); - - /** - * Unless the test author requests differently, run as "nobody", and give each collection of - * tests its own unique PID. - */ - int mUid = FIRST_APPLICATION_UID; - int mPid = sNextPid.getAndIncrement(); - - String mTestPackageName; - String mTargetPackageName; - - int mTargetSdkLevel; - - final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties(); - - final List<Class<?>> mServicesRequired = new ArrayList<>(); - - volatile Context mInstContext; - volatile Context mTargetContext; - volatile Instrumentation mInstrumentation; - /** * Stores internal states / methods associated with this config that's only needed in * junit-impl. */ - final RavenwoodConfigState mState = new RavenwoodConfigState(this); private RavenwoodConfig() { } @@ -159,34 +124,11 @@ public final class RavenwoodConfig { return this; } - Builder setSystemPropertyImmutableReal(@NonNull String key, - @Nullable Object value) { - mConfig.mSystemProperties.setValue(key, value); - mConfig.mSystemProperties.setAccessReadOnly(key); - return this; - } - - Builder setSystemPropertyMutableReal(@NonNull String key, - @Nullable Object value) { - mConfig.mSystemProperties.setValue(key, value); - mConfig.mSystemProperties.setAccessReadWrite(key); - return this; - } - /** - * Configure the set of system services that are required for this test to operate. - * - * For example, passing {@code android.hardware.SerialManager.class} as an argument will - * ensure that the underlying service is created, initialized, and ready to use for the - * duration of the test. The {@code SerialManager} instance can be obtained via - * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and - * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}. + * @deprecated no longer used. All supported services are available. */ + @Deprecated public Builder setServicesRequired(@NonNull Class<?>... services) { - mConfig.mServicesRequired.clear(); - for (Class<?> service : services) { - mConfig.mServicesRequired.add(service); - } return this; } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 5681a9040f63..e49d3d934e9f 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -92,19 +92,11 @@ public final class RavenwoodRule implements TestRule { } } - private final RavenwoodConfig mConfiguration; - - public RavenwoodRule() { - mConfiguration = new RavenwoodConfig.Builder().build(); - } - - private RavenwoodRule(RavenwoodConfig config) { - mConfiguration = config; - } + final RavenwoodTestProperties mProperties = new RavenwoodTestProperties(); public static class Builder { - private final RavenwoodConfig.Builder mBuilder = - new RavenwoodConfig.Builder(); + + private final RavenwoodRule mRule = new RavenwoodRule(); public Builder() { } @@ -152,7 +144,8 @@ public final class RavenwoodRule implements TestRule { * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) { - mBuilder.setSystemPropertyImmutableReal(key, value); + mRule.mProperties.setValue(key, value); + mRule.mProperties.setAccessReadOnly(key); return this; } @@ -167,26 +160,21 @@ public final class RavenwoodRule implements TestRule { * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) { - mBuilder.setSystemPropertyMutableReal(key, value); + mRule.mProperties.setValue(key, value); + mRule.mProperties.setAccessReadWrite(key); return this; } /** - * Configure the set of system services that are required for this test to operate. - * - * For example, passing {@code android.hardware.SerialManager.class} as an argument will - * ensure that the underlying service is created, initialized, and ready to use for the - * duration of the test. The {@code SerialManager} instance can be obtained via - * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and - * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}. + * @deprecated no longer used. All supported services are available. */ + @Deprecated public Builder setServicesRequired(@NonNull Class<?>... services) { - mBuilder.setServicesRequired(services); return this; } public RavenwoodRule build() { - return new RavenwoodRule(mBuilder.build()); + return mRule; } } @@ -227,7 +215,7 @@ public final class RavenwoodRule implements TestRule { @Override public Statement apply(Statement base, Description description) { - if (!RavenwoodConfig.isOnRavenwood()) { + if (!IS_ON_RAVENWOOD) { return base; } return new Statement() { @@ -296,8 +284,4 @@ public final class RavenwoodRule implements TestRule { public static RavenwoodPrivate private$ravenwood() { return sRavenwoodPrivate; } - - RavenwoodConfig getConfiguration() { - return mConfiguration; - } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java new file mode 100644 index 000000000000..66a26b511213 --- /dev/null +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.platform.test.ravenwood; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A class to store system properties defined by tests. + */ +public class RavenwoodTestProperties { + final Map<String, String> mValues = new HashMap<>(); + + /** Set of additional keys that should be considered readable */ + final Set<String> mKeyReadable = new HashSet<>(); + + /** Set of additional keys that should be considered writable */ + final Set<String> mKeyWritable = new HashSet<>(); + + public void setValue(String key, Object value) { + final String valueString = (value == null) ? null : String.valueOf(value); + if ((valueString == null) || valueString.isEmpty()) { + mValues.remove(key); + } else { + mValues.put(key, valueString); + } + } + + public void setAccessNone(String key) { + mKeyReadable.remove(key); + mKeyWritable.remove(key); + } + + public void setAccessReadOnly(String key) { + mKeyReadable.add(key); + mKeyWritable.remove(key); + } + + public void setAccessReadWrite(String key) { + mKeyReadable.add(key); + mKeyWritable.add(key); + } +} diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java index 7b940b423b69..9a78989dad55 100644 --- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java +++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java @@ -56,7 +56,11 @@ public class RavenwoodRuntimeNative { public static native boolean setSystemProperty(String key, String value); - public static native void clearSystemProperties(); + public static native boolean removeSystemProperty(String key); + + public static void clearSystemProperties() { + removeSystemProperty(null); + } public static native int gettid(); diff --git a/ravenwood/runtime-jni/jni_helper.h b/ravenwood/runtime-jni/jni_helper.h index 561fb3beda6b..25d75193de09 100644 --- a/ravenwood/runtime-jni/jni_helper.h +++ b/ravenwood/runtime-jni/jni_helper.h @@ -26,6 +26,7 @@ constexpr const char* kCommonUtils = "com/android/ravenwood/common/RavenwoodCommonUtils"; constexpr const char* kRuntimeEnvController = "android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController"; +constexpr const char* kRunnerState = "android/platform/test/ravenwood/RavenwoodRunnerState"; constexpr const char* kRuntimeNative = "com/android/ravenwood/RavenwoodRuntimeNative"; // We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars diff --git a/ravenwood/runtime-jni/ravenwood_sysprop.cpp b/ravenwood/runtime-jni/ravenwood_sysprop.cpp index aafc4268d782..a78aa8da9052 100644 --- a/ravenwood/runtime-jni/ravenwood_sysprop.cpp +++ b/ravenwood/runtime-jni/ravenwood_sysprop.cpp @@ -117,7 +117,7 @@ void __system_property_read_callback(const prop_info* pi, // ---- JNI ---- static JavaVM* gVM = nullptr; -static jclass gEnvController = nullptr; +static jclass gRunnerState = nullptr; static jmethodID gCheckSystemPropertyAccess; static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) { @@ -128,11 +128,11 @@ static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) { // Call back into Java code to check property access static void check_system_property_access(const char* key, bool write) { - if (gVM != nullptr && gEnvController != nullptr) { + if (gVM != nullptr && gRunnerState != nullptr) { JNIEnv* env; if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) { ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key); - env->CallStaticVoidMethod(gEnvController, gCheckSystemPropertyAccess, + env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess, env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE); return; } @@ -155,16 +155,29 @@ static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE; } -static void clearSystemProperties(JNIEnv*, jclass) { +static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) { std::lock_guard lock(g_properties_lock); - g_properties.clear(); + + if (javaKey == nullptr) { + g_properties.clear(); + return JNI_TRUE; + } else { + ScopedUtfChars key(env, javaKey); + auto it = g_properties.find(key); + if (it != g_properties.end()) { + g_properties.erase(it); + return JNI_TRUE; + } else { + return JNI_FALSE; + } + } } static const JNINativeMethod sMethods[] = { {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary}, {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty}, {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty}, - {"clearSystemProperties", "()V", (void*)clearSystemProperties}, + {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty}, }; extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { @@ -174,9 +187,9 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { gVM = vm; // Fetch several references for future use - gEnvController = FindGlobalClassOrDie(env, kRuntimeEnvController); + gRunnerState = FindGlobalClassOrDie(env, kRunnerState); gCheckSystemPropertyAccess = - GetStaticMethodIDOrDie(env, gEnvController, "checkSystemPropertyAccess", + GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess", "(Ljava/lang/String;Z)V"); // Expose raw property methods as JNI methods diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java deleted file mode 100644 index c25d2b4cbc4d..000000000000 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.ravenwoodtest.bivalenttest; - -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodRule; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Assume; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; - -/** - * Make sure having multiple RavenwoodRule's is detected. - * (But only when running on ravenwod. Otherwise it'll be ignored.) - */ -@RunWith(AndroidJUnit4.class) -public class RavenwoodMultipleRuleTest { - - @Rule(order = Integer.MIN_VALUE) - public final ExpectedException mExpectedException = ExpectedException.none(); - - @Rule - public final RavenwoodRule mRavenwood1 = new RavenwoodRule(); - - @Rule - public final RavenwoodRule mRavenwood2 = new RavenwoodRule(); - - public RavenwoodMultipleRuleTest() { - // We can't call it within the test method because the exception happens before - // calling the method, so set it up here. - if (RavenwoodConfig.isOnRavenwood()) { - mExpectedException.expectMessage("Multiple nesting RavenwoodRule"); - } - } - - @Test - public void testMultipleRulesNotAllowed() { - Assume.assumeTrue(RavenwoodConfig.isOnRavenwood()); - } -} diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java new file mode 100644 index 000000000000..f9e73db23740 --- /dev/null +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ravenwoodtest.runnercallbacktests; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.SystemProperties; +import android.platform.test.annotations.NoRavenizer; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +/** + * Test for RavenwoodRule. + */ +@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner. +public class RavenwoodRuleValidationTest extends RavenwoodRunnerTestBase { + + public static class RuleInBaseClass { + static String PROPERTY_KEY = "debug.ravenwood.prop.in.base"; + static String PROPERTY_VAL = "ravenwood"; + @Rule + public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder() + .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build(); + } + + /** + * Make sure that RavenwoodRule in a base class takes effect. + */ + @RunWith(AndroidJUnit4.class) + // CHECKSTYLE:OFF + @Expected(""" + testRunStarted: classes + testSuiteStarted: classes + testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest + testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest) + testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest) + testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest + testSuiteFinished: classes + testRunFinished: 1,0,0,0 + """) + // CHECKSTYLE:ON + public static class RuleInBaseClassSuccessTest extends RuleInBaseClass { + + @Test + public void testRuleInBaseClass() { + assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL); + } + } + + /** + * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}. + */ + public abstract static class RuleWithDifferentTypeInBaseClass { + static String PROPERTY_KEY = "debug.ravenwood.prop.in.base.different.type"; + static String PROPERTY_VAL = "ravenwood"; + @Rule + public final TestRule mRavenwood1 = new RavenwoodRule.Builder() + .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build(); + } + + /** + * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not + */ + @RunWith(AndroidJUnit4.class) + // CHECKSTYLE:OFF + @Expected(""" + testRunStarted: classes + testSuiteStarted: classes + testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest + testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) + testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) + testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest + testSuiteFinished: classes + testRunFinished: 1,0,0,0 + """) + // CHECKSTYLE:ON + public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass { + + @Test + public void testRuleInBaseClass() { + assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL); + } + } +} diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java deleted file mode 100644 index f94b98bc1fb8..000000000000 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.ravenwoodtest.runnercallbacktests; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.NoRavenizer; -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodRule; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; - - -/** - * Test for @Config field extraction and validation. - * - * TODO(b/377765941) Most of the tests here will be obsolete and deleted with b/377765941, but - * some of the tests may need to be re-implemented one way or another. (e.g. the package name - * test.) Until that happens, we'll keep all tests here but add an {@code @Ignore} instead. - */ -@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner. -public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase { - public abstract static class ConfigInBaseClass { - static String PACKAGE_NAME = "com.ConfigInBaseClass"; - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder() - .setPackageName(PACKAGE_NAME).build(); - } - - /** - * Make sure a config in the base class is detected. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest - testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest) - testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest - testSuiteFinished: classes - testRunFinished: 1,0,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class ConfigInBaseClassTest extends ConfigInBaseClass { - @Test - public void test() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME); - } - } - - /** - * Make sure a config in the base class is detected. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest - testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest) - testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest - testSuiteFinished: classes - testRunFinished: 1,0,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class ConfigOverridingTest extends ConfigInBaseClass { - static String PACKAGE_NAME_OVERRIDE = "com.ConfigOverridingTest"; - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder() - .setPackageName(PACKAGE_NAME_OVERRIDE).build(); - - @Test - public void test() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME_OVERRIDE); - } - } - - /** - * Test to make sure that if a test has a config error, the failure would be reported from - * each test method. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class ErrorMustBeReportedFromEachTest { - @RavenwoodConfig.Config - private static RavenwoodConfig sConfig = // Invalid because it's private. - new RavenwoodConfig.Builder().build(); - - @Test - public void testMethod1() { - } - - @Test - public void testMethod2() { - } - - @Test - public void testMethod3() { - } - } - - /** - * Invalid because there are two @Config's. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) - testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfig.Config - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class DuplicateConfigTest { - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig1 = - new RavenwoodConfig.Builder().build(); - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig2 = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * @Config's must be static. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class NonStaticConfigTest { - - @RavenwoodConfig.Config - public RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * @Config's must be public. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class NonPublicConfigTest { - - @RavenwoodConfig.Config - RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * @Config's must be of type RavenwoodConfig. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest) - testFailure: Field com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.WrongTypeConfigTest.sConfig has @RavenwoodConfig.Config but type is not RavenwoodConfig - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WrongTypeConfigTest { - - @RavenwoodConfig.Config - public static Object sConfig = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - - } - - /** - * @Rule must be of type RavenwoodRule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest - testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest) - testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it. - testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WrongTypeRuleTest { - - @Rule - public TestRule mRule = new RavenwoodRule.Builder().build(); - - @Test - public void testConfig() { - } - - } - - /** - * Config can't be used with a (instance) Rule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) - testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig. - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WithInstanceRuleTest { - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Rule - public RavenwoodRule mRule = new RavenwoodRule.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * Config can't be used with a (static) Rule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest) - testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4 - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WithStaticRuleTest { - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Rule - public static RavenwoodRule sRule = new RavenwoodRule.Builder().build(); - - @Test - public void testConfig() { - } - } - - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest - testStarted: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest) - testFailure: Multiple nesting RavenwoodRule's are detected in the same class, which is not supported. - testFinished: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class DuplicateRulesTest { - - @Rule - public final RavenwoodRule mRavenwood1 = new RavenwoodRule(); - - @Rule - public final RavenwoodRule mRavenwood2 = new RavenwoodRule(); - - @Test - public void testMultipleRulesNotAllowed() { - } - } - - public static class RuleInBaseClass { - static String PACKAGE_NAME = "com.RuleInBaseClass"; - @Rule - public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder() - .setPackageName(PACKAGE_NAME).build(); - } - - /** - * Make sure that RavenwoodRule in a base class takes effect. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest - testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest) - testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest - testSuiteFinished: classes - testRunFinished: 1,0,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class RuleInBaseClassSuccessTest extends RuleInBaseClass { - - @Test - public void testRuleInBaseClass() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME); - } - } - - /** - * Make sure that having a config and a rule in a base class should fail. - * RavenwoodRule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) - testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig. - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class ConfigWithRuleInBaseClassTest extends RuleInBaseClass { - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build(); - - @Test - public void test() { - } - } - - /** - * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}. - */ - public abstract static class RuleWithDifferentTypeInBaseClass { - static String PACKAGE_NAME = "com.RuleWithDifferentTypeInBaseClass"; - @Rule - public final TestRule mRavenwood1 = new RavenwoodRule.Builder() - .setPackageName(PACKAGE_NAME).build(); - } - - /** - * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest - testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) - testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it. - testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass { - - @Test - public void testRuleInBaseClass() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME); - } - } - - /** - * Make sure that having a config and a rule in a base class should fail, even if the field type is not - * RavenwoodRule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest - testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest) - testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it. - testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class ConfigWithRuleWithDifferentTypeInBaseClassTest extends RuleWithDifferentTypeInBaseClass { - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build(); - - @Test - public void test() { - } - } -} diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java index 8e04b698c9d9..271c27f6ae93 100644 --- a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java @@ -15,7 +15,6 @@ */ package com.android.ravenwoodtest.runtimetest; -import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.os.Process.FIRST_APPLICATION_UID; import static org.junit.Assert.assertEquals; @@ -23,7 +22,6 @@ import static org.junit.Assert.assertEquals; import android.os.Binder; import android.os.Build; import android.os.Process; -import android.platform.test.ravenwood.RavenwoodConfig; import android.system.Os; import com.android.ravenwood.RavenwoodRuntimeState; @@ -34,13 +32,6 @@ import org.junit.Test; public class IdentityTest { - @RavenwoodConfig.Config - public static final RavenwoodConfig sConfig = - new RavenwoodConfig.Builder() - .setTargetSdkLevel(UPSIDE_DOWN_CAKE) - .setProcessApp() - .build(); - @Test public void testUid() { assertEquals(FIRST_APPLICATION_UID, RavenwoodRuntimeState.sUid); @@ -60,7 +51,7 @@ public class IdentityTest { @Test public void testTargetSdkLevel() { assertEquals(Build.VERSION_CODES.CUR_DEVELOPMENT, RavenwoodRuntimeState.CUR_DEVELOPMENT); - assertEquals(UPSIDE_DOWN_CAKE, RavenwoodRuntimeState.sTargetSdkLevel); - assertEquals(UPSIDE_DOWN_CAKE, VMRuntime.getRuntime().getTargetSdkVersion()); + assertEquals(RavenwoodRuntimeState.sTargetSdkLevel, + VMRuntime.getRuntime().getTargetSdkVersion()); } } diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java new file mode 100644 index 000000000000..70bf204ed823 --- /dev/null +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ravenwoodtest.runtimetest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.os.SystemProperties; +import android.platform.test.ravenwood.RavenwoodRule; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runners.model.Statement; + +public class SystemPropertyTest { + + private static final String PROP_KEY_1 = "debug.ravenwood.prop1"; + private static final String PROP_VAL_1 = "ravenwood.1"; + private static final String PROP_KEY_2 = "debug.ravenwood.prop2"; + private static final String PROP_VAL_2 = "ravenwood.2"; + private static final String PROP_KEY_3 = "debug.ravenwood.prop3"; + private static final String PROP_VAL_3 = "ravenwood.3"; + private static final String PROP_VAL_4 = "ravenwood.4"; + + @ClassRule(order = 0) + public static TestRule mCheckClassRule = (base, description) -> new Statement() { + @Override + public void evaluate() throws Throwable { + assertTrue(SystemProperties.get(PROP_KEY_1).isEmpty()); + assertTrue(SystemProperties.get(PROP_KEY_3).isEmpty()); + try { + base.evaluate(); + } finally { + assertTrue(SystemProperties.get(PROP_KEY_1).isEmpty()); + assertTrue(SystemProperties.get(PROP_KEY_3).isEmpty()); + } + } + }; + + @ClassRule(order = 1) + public static RavenwoodRule mClassRule = new RavenwoodRule.Builder() + .setSystemPropertyImmutable(PROP_KEY_1, PROP_VAL_1) + .setSystemPropertyImmutable(PROP_KEY_3, PROP_VAL_4) + .build(); + + @Rule(order = 0) + public TestRule mCheckRule = (base, description) -> new Statement() { + @Override + public void evaluate() throws Throwable { + assertTrue(SystemProperties.get(PROP_KEY_2).isEmpty()); + assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_4); + try { + base.evaluate(); + } finally { + assertTrue(SystemProperties.get(PROP_KEY_2).isEmpty()); + assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_4); + } + } + }; + + @Rule(order = 1) + public RavenwoodRule mRule = new RavenwoodRule.Builder() + .setSystemPropertyImmutable(PROP_KEY_2, PROP_VAL_2) + .setSystemPropertyImmutable(PROP_KEY_3, PROP_VAL_3) + .build(); + + @Test + public void testRavenwoodRuleSetProperty() { + assertEquals(SystemProperties.get(PROP_KEY_1), PROP_VAL_1); + assertEquals(SystemProperties.get(PROP_KEY_2), PROP_VAL_2); + assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_3); + } +} diff --git a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java index 4aae1e11b72e..e83a247bd769 100644 --- a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java @@ -24,8 +24,6 @@ import static org.junit.Assert.fail; import android.content.Context; import android.hardware.SerialManager; import android.hardware.SerialManagerInternal; -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodConfig.Config; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; @@ -42,12 +40,6 @@ import org.junit.runner.RunWith; public class RavenwoodServicesTest { private static final String TEST_VIRTUAL_PORT = "virtual:example"; - @Config - public static final RavenwoodConfig sRavenwood = new RavenwoodConfig.Builder() - .setProcessSystem() - .setServicesRequired(SerialManager.class) - .build(); - private Context mContext; @Before diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index d6fc6e461edc..e1b6c9c5aa42 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -409,10 +409,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo final int eventSource = event.getSource(); final int displayId = event.getDisplayId(); if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) { - if (!Flags.doNotResetKeyEventState()) { - state.reset(); - clearEventStreamHandler(displayId, eventSource); - } if (DEBUG) { Slog.d(TAG, "Not processing event " + event); } @@ -1180,18 +1176,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) { - if (Flags.alwaysAllowObservingTouchEvents()) { - final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); - if (isTouchEvent && !canShareGenericTouchEvent()) { - return false; - } - final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0; - } - // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing - // touch exploration. - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) - && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { + final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); + if (isTouchEvent && !canShareGenericTouchEvent()) { return false; } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; @@ -1199,21 +1185,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) { - if (Flags.alwaysAllowObservingTouchEvents()) { - final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0; - } - // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing - // touch exploration. - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) - && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { - return false; - } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources - & mCombinedMotionEventObservedSources - & eventSourceWithoutClass) - != 0; + return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0; } private boolean canShareGenericTouchEvent() { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index c210e726fc12..71a0fc453a95 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3797,13 +3797,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() && userState.isMagnificationTwoFingerTripleTapEnabledLocked())); - final boolean createConnectionForCurrentCapability = - com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - || (userState.getMagnificationCapabilitiesLocked() - != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - - final boolean connect = (shortcutEnabled && createConnectionForCurrentCapability) - || userHasMagnificationServicesLocked(userState); + final boolean connect = shortcutEnabled || userHasMagnificationServicesLocked(userState); getMagnificationConnectionManager().requestConnection(connect); } @@ -4852,8 +4846,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub getMagnificationConnectionManager().setConnection(connection); - if (com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - && connection == null + if (connection == null && mMagnificationController.isFullScreenMagnificationControllerInitialized()) { // Since the connection does not exist, the system ui cannot provide the border // implementation for fullscreen magnification. So we call reset to deactivate the @@ -6548,8 +6541,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Only continue setting up the packages if the service has been initialized. // See: b/340927041 - if (Flags.skipPackageChangeBeforeUserSwitch() - && !mManagerService.isServiceInitializedLocked()) { + if (!mManagerService.isServiceInitializedLocked()) { Slog.w(LOG_TAG, "onSomePackagesChanged: service not initialized, skip the callback."); return; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 5cbe0c4fe4d2..8b870dbaa100 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -433,10 +433,24 @@ public class AccessibilityWindowManager { return Collections.emptyList(); } - private void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, - IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows) { - // TODO(b/322444245): no longer need to get a lock. + /** + * Called when the windows for accessibility changed. + * + * @param forceSend Send the windows for accessibility even if they haven't + * changed. + * @param topFocusedDisplayId The display Id which has the top focused window. + * @param topFocusedWindowToken The window token of top focused window. + * @param screenSize The size of the display that the change happened. + * @param accessibilityWindows The windows for accessibility. + */ + @Override + public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId, + @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize, + @NonNull List<AccessibilityWindow> accessibilityWindows) { synchronized (mLock) { + final List<WindowInfo> windows = + createWindowInfoListLocked(screenSize, accessibilityWindows); + if (DEBUG) { Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, " + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId, @@ -451,14 +465,15 @@ public class AccessibilityWindowManager { Slogf.i(LOG_TAG, "%d windows changed: %s", windows.size(), windowsInfo); } } - if (shouldUpdateWindowsLocked(forceSend, windows)) { + + if (forceSend || shouldUpdateWindowsLocked(windows)) { mTopFocusedDisplayId = topFocusedDisplayId; if (!isProxyed(topFocusedDisplayId)) { mLastNonProxyTopFocusedDisplayId = topFocusedDisplayId; } mTopFocusedWindowToken = topFocusedWindowToken; if (DEBUG) { - Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for " + Slogf.d(LOG_TAG, "onAccessibilityWindowsChanged(): updating windows for " + "display %d and token %s", topFocusedDisplayId, topFocusedWindowToken); } @@ -468,37 +483,14 @@ public class AccessibilityWindowManager { windows); // Someone may be waiting for the windows - advertise it. mLock.notifyAll(); - } - else if (DEBUG) { - Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for " + } else if (DEBUG) { + Slogf.d(LOG_TAG, "onAccessibilityWindowsChanged(): NOT updating windows for " + "display %d and token %s", topFocusedDisplayId, topFocusedWindowToken); } } } - /** - * Called when the windows for accessibility changed. - * - * @param forceSend Send the windows for accessibility even if they haven't - * changed. - * @param topFocusedDisplayId The display Id which has the top focused window. - * @param topFocusedWindowToken The window token of top focused window. - * @param screenSize The size of the display that the change happened. - * @param windows The windows for accessibility. - */ - @Override - public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId, - @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize, - @NonNull List<AccessibilityWindow> windows) { - synchronized (mLock) { - final List<WindowInfo> windowInfoList = - createWindowInfoListLocked(screenSize, windows); - onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId, - topFocusedWindowToken, windowInfoList); - } - } - private List<WindowInfo> createWindowInfoListLocked(@NonNull Point screenSize, @NonNull List<AccessibilityWindow> visibleWindows) { final Set<IBinder> addedWindows = new ArraySet<>(); @@ -650,12 +642,7 @@ public class AccessibilityWindowManager { windowInfo.locales = attributes.getLocales(); } - private boolean shouldUpdateWindowsLocked(boolean forceSend, - @NonNull List<WindowInfo> windows) { - if (forceSend) { - return true; - } - + private boolean shouldUpdateWindowsLocked(@NonNull List<WindowInfo> windows) { final int windowCount = windows.size(); if (VERBOSE) { Slogf.v(LOG_TAG, diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 0ed239e442e7..0cbbf6da022b 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -240,10 +240,7 @@ public class TouchExplorer extends BaseEventStreamTransformation } private void clear(MotionEvent event, int policyFlags) { - if (mState.isTouchExploring() || Flags.sendHoverEventsBasedOnEventStream()) { - // If a touch exploration gesture is in progress send events for its end. - sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); - } + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); mDraggingPointerId = INVALID_POINTER_ID; // Send exit to any pointers that we have delivered as part of delegating or dragging. mDispatcher.sendUpForInjectedDownPointers(event, policyFlags); @@ -562,10 +559,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // clear any hover events that might have been queued and never sent. mSendHoverEnterAndMoveDelayed.clear(); mSendHoverExitDelayed.cancel(); - // If a touch exploration gesture is in progress send events for its end. - if (mState.isTouchExploring() || Flags.sendHoverEventsBasedOnEventStream()) { - sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); - } + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); if (mState.isClear()) { if (!mSendHoverEnterAndMoveDelayed.isPending()) { // Queue a delayed transition to STATE_TOUCH_EXPLORING. @@ -1599,9 +1593,7 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mEvents.size() == 0) { return; } - if (Flags.sendHoverEventsBasedOnEventStream()) { - sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); - } + sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); // Send an accessibility event to announce the touch exploration start. mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START); if (isSendMotionEventsEnabled()) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index d3d80e12313f..11b8ccb70dfb 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -699,10 +699,9 @@ public class FullScreenMagnificationController implements if (!mRegistered) { return false; } - // If the border implementation is on system ui side but the connection is not + // The border implementation is on system ui side but the connection is not // established, the fullscreen magnification should not work. - if (com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - && !mMagnificationConnectionStateSupplier.get()) { + if (!mMagnificationConnectionStateSupplier.get()) { return false; } if (DEBUG) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 51c4305061f8..058b2be5f4b3 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -51,7 +51,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.wm.WindowManagerInternal; -import com.android.window.flags.Flags; import java.util.concurrent.Executor; @@ -634,10 +633,8 @@ public class MagnificationController implements MagnificationConnectionManager.C @Override public void onFullScreenMagnificationActivationState(int displayId, boolean activated) { - if (Flags.alwaysDrawMagnificationFullscreenBorder()) { - getMagnificationConnectionManager() - .onFullscreenMagnificationActivationChanged(displayId, activated); - } + getMagnificationConnectionManager() + .onFullscreenMagnificationActivationChanged(displayId, activated); if (activated) { synchronized (mLock) { diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig index b3fe5f234bc2..444844121190 100644 --- a/services/autofill/features.aconfig +++ b/services/autofill/features.aconfig @@ -37,3 +37,10 @@ flag { description: "Improvements for Fill Dialog, including deprecation of pre-trigger API's" bug: "336223371" } + +flag { + name: "add_last_focused_id_to_fill_event_history" + namespace: "autofill" + description: "Adds focused id to each event that's part of the fill event history" + bug: "334141398" +} diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index b52c65054e51..cd4ace2e3835 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -900,13 +900,13 @@ final class AutofillManagerServiceImpl * Updates the last fill selection when an authentication was selected. */ void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState, - int uiType) { + int uiType, @Nullable AutofillId focusedId) { synchronized (mLock) { if (isValidEventLocked("setAuthenticationSelected()", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null, null, null, null, null, null, null, - NO_SAVE_UI_REASON_NONE, uiType)); + NO_SAVE_UI_REASON_NONE, uiType, focusedId)); } } } @@ -915,13 +915,13 @@ final class AutofillManagerServiceImpl * Updates the last fill selection when an dataset authentication was selected. */ void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId, - @Nullable Bundle clientState, int uiType) { + @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) { synchronized (mLock) { if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset, clientState, null, null, null, null, null, null, null, null, - NO_SAVE_UI_REASON_NONE, uiType)); + NO_SAVE_UI_REASON_NONE, uiType, focusedId)); } } } @@ -933,7 +933,7 @@ final class AutofillManagerServiceImpl synchronized (mLock) { if (isValidEventLocked("logSaveShown()", sessionId)) { mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null, - null, null, null, null, null, null, null)); + null, null, null, null, null, null, null, /* focusedId= */ null)); } } } @@ -942,13 +942,13 @@ final class AutofillManagerServiceImpl * Updates the last fill response when a dataset was selected. */ void logDatasetSelected(@Nullable String selectedDataset, int sessionId, - @Nullable Bundle clientState, int uiType) { + @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) { synchronized (mLock) { if (isValidEventLocked("logDatasetSelected()", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null, null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE, - uiType)); + uiType, focusedId)); } } } @@ -956,13 +956,14 @@ final class AutofillManagerServiceImpl /** * Updates the last fill response when a dataset is shown. */ - void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType) { + void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType, + @Nullable AutofillId focusedId) { synchronized (mLock) { if (isValidEventLocked("logDatasetShown", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE, - uiType)); + uiType, focusedId)); } } } @@ -970,7 +971,8 @@ final class AutofillManagerServiceImpl /** * Updates the last fill response when a view was entered. */ - void logViewEntered(int sessionId, @Nullable Bundle clientState) { + void logViewEntered(int sessionId, @Nullable Bundle clientState, + @Nullable AutofillId focusedId) { synchronized (mLock) { if (!isValidEventLocked("logViewEntered", sessionId)) { return; @@ -988,7 +990,7 @@ final class AutofillManagerServiceImpl mEventHistory.addEvent( new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null, - null, null, null, null, null, null, null)); + null, null, null, null, null, null, null, focusedId)); } } @@ -1001,7 +1003,8 @@ final class AutofillManagerServiceImpl } mAugmentedAutofillEventHistory.addEvent( new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset, - clientState, null, null, null, null, null, null, null, null)); + clientState, null, null, null, null, null, null, null, null, + /* focusedId= */ null)); } } @@ -1014,7 +1017,7 @@ final class AutofillManagerServiceImpl } mAugmentedAutofillEventHistory.addEvent( new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null, - null, null, null, null, null, null)); + null, null, null, null, null, null, /* focusedId= */ null)); } } @@ -1029,7 +1032,7 @@ final class AutofillManagerServiceImpl mAugmentedAutofillEventHistory.addEvent( new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE, - UI_TYPE_INLINE)); + UI_TYPE_INLINE, /* focusedId= */ null)); } } @@ -1113,7 +1116,8 @@ final class AutofillManagerServiceImpl clientState, selectedDatasets, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, - detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason)); + detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason, + /* focusedId= */ null)); } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 8f12b1db8f29..6b227d7a876e 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1871,7 +1871,7 @@ final class Session if (mLogViewEntered) { mLogViewEntered = false; - mService.logViewEntered(id, null); + mService.logViewEntered(id, null, mCurrentViewId); } } @@ -2775,9 +2775,9 @@ final class Session forceRemoveFromServiceLocked(); return; } + mService.setAuthenticationSelected(id, mClientState, uiType, mCurrentViewId); } - mService.setAuthenticationSelected(id, mClientState, uiType); final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); mHandler.sendMessage( @@ -2850,7 +2850,7 @@ final class Session if (!mLoggedInlineDatasetShown) { // Chip inflation already logged, do not log again. // This is needed because every chip inflation will call this. - mService.logDatasetShown(this.id, mClientState, uiType); + mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId); Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown); } mLoggedInlineDatasetShown = true; @@ -2858,7 +2858,7 @@ final class Session mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown); // Explicitly sets maybeSetSuggestionPresentedTimestampMs mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs(); - mService.logDatasetShown(this.id, mClientState, uiType); + mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId); Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown); } } @@ -5139,7 +5139,7 @@ final class Session // so this calling logViewEntered will be a nop. // Calling logViewEntered() twice will only log it once // TODO(271181979): this is broken for multiple partitions - mService.logViewEntered(this.id, null); + mService.logViewEntered(this.id, null, mCurrentViewId); } // If this is the first time view is entered for inline, the last @@ -6657,7 +6657,8 @@ final class Session // Autofill it directly... if (dataset.getAuthentication() == null) { if (generateEvent) { - mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType); + mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType, + mCurrentViewId); } if (mCurrentViewId != null) { mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); @@ -6667,7 +6668,8 @@ final class Session } // ...or handle authentication. - mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType); + mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType, + mCurrentViewId); mPresentationStatsEventLogger.maybeSetAuthenticationType( AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); // does not matter the value of isPrimary because null response won't be overridden. diff --git a/services/core/Android.bp b/services/core/Android.bp index aea16b08df49..f88aa9b4579c 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -178,6 +178,7 @@ java_library_static { static_libs: [ "android.frameworks.vibrator-V1-java", // AIDL + "android.frameworks.devicestate-V1-java", // AIDL "android.hardware.authsecret-V1.0-java", "android.hardware.authsecret-V1-java", "android.hardware.boot-V1.0-java", // HIDL @@ -188,7 +189,7 @@ java_library_static { "android.hardware.health-V1.0-java", // HIDL "android.hardware.health-V2.0-java", // HIDL "android.hardware.health-V2.1-java", // HIDL - "android.hardware.health-V3-java", // AIDL + "android.hardware.health-V4-java", // AIDL "android.hardware.health-translate-java", "android.hardware.light-V1-java", "android.hardware.security.authgraph-V1-java", diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 3dcca1433dec..4cf17ae3984d 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -69,6 +69,7 @@ import android.service.battery.BatteryServiceDumpProto; import android.sysprop.PowerProperties; import android.util.EventLog; import android.util.Slog; +import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -303,6 +304,17 @@ public final class BatteryService extends SystemService { */ @VisibleForTesting public long mLastBroadcastVoltageUpdateTime; + /** + * Time when the max charging current was updated last by HAL and we sent the + * {@link Intent#ACTION_BATTERY_CHANGED} broadcast. + * Note: This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast + * so it is possible that max current was updated but we did not send the broadcast so in that + * case we do not update the time. + */ + @VisibleForTesting + public long mLastBroadcastMaxChargingCurrentUpdateTime; + + private boolean mIsFirstBatteryChangedUpdate = true; private Led mLed; @@ -350,16 +362,21 @@ public final class BatteryService extends SystemService { private static final int ABSOLUTE_DECI_CELSIUS_DIFF_FOR_TEMP_UPDATE = 10; /** * This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We - * only send the broadcast if the last voltage was updated at least 20s seconds back and has a + * only send the broadcast if the last voltage was updated at least 20 seconds back and has a * fluctuation of at least 1%. */ private static final int TIME_DIFF_FOR_VOLTAGE_UPDATE_MS = 20000; /** * The value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We - * only send the broadcast if the last voltage was updated at least 20s seconds back and has a + * only send the broadcast if the last voltage was updated at least 20 seconds back and has a * fluctuation of at least 1%. */ private static final float BASE_POINT_DIFF_FOR_VOLTAGE_UPDATE = 0.01f; + /** + * This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We + * only send the broadcast if the last max charging current was updated at least 5 seconds back. + */ + private static final int TIME_DIFF_FOR_MAX_CHARGING_CURRENT_UPDATE_MS = 5000; private final Handler.Callback mLocalCallback = msg -> { switch (msg.what) { @@ -1252,8 +1269,10 @@ public final class BatteryService extends SystemService { if (!com.android.server.flags.Flags.rateLimitBatteryChangedBroadcast()) { return false; } - if (mLastBroadcastBatteryVoltage == 0 || mLastBroadcastBatteryTemperature == 0) { + if (mIsFirstBatteryChangedUpdate) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + mIsFirstBatteryChangedUpdate = false; return false; } @@ -1261,13 +1280,14 @@ public final class BatteryService extends SystemService { mLastBroadcastBatteryVoltage != mHealthInfo.batteryVoltageMillivolts; final boolean temperatureUpdated = mLastBroadcastBatteryTemperature != mHealthInfo.batteryTemperatureTenthsCelsius; + final boolean maxChargingCurrentUpdated = + mLastBroadcastMaxChargingCurrent != mHealthInfo.maxChargingCurrentMicroamps; final boolean otherStatesUpdated = forceUpdate || mHealthInfo.batteryStatus != mLastBroadcastBatteryStatus || mHealthInfo.batteryHealth != mLastBroadcastBatteryHealth || mHealthInfo.batteryPresent != mLastBroadcastBatteryPresent || mHealthInfo.batteryLevel != mLastBroadcastBatteryLevel || mPlugType != mLastBroadcastPlugType - || mHealthInfo.maxChargingCurrentMicroamps != mLastBroadcastMaxChargingCurrent || mHealthInfo.maxChargingVoltageMicrovolts != mLastBroadcastMaxChargingVoltage || mInvalidCharger != mLastBroadcastInvalidCharger || mHealthInfo.batteryCycleCount != mLastBroadcastBatteryCycleCount @@ -1280,6 +1300,9 @@ public final class BatteryService extends SystemService { if (voltageUpdated) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); } + if (maxChargingCurrentUpdated) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + } return false; } @@ -1295,6 +1318,9 @@ public final class BatteryService extends SystemService { >= TIME_DIFF_FOR_VOLTAGE_UPDATE_MS) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); + if (maxChargingCurrentUpdated) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + } return false; } @@ -1307,6 +1333,20 @@ public final class BatteryService extends SystemService { if (voltageUpdated) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); } + if (maxChargingCurrentUpdated) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + } + return false; + } + + if (maxChargingCurrentUpdated + && SystemClock.elapsedRealtime() - mLastBroadcastMaxChargingCurrentUpdateTime + >= TIME_DIFF_FOR_MAX_CHARGING_CURRENT_UPDATE_MS) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + + if (voltageUpdated) { + mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); + } return false; } @@ -1615,6 +1655,9 @@ public final class BatteryService extends SystemService { pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline); pw.println(" Dock powered: " + mHealthInfo.chargerDockOnline); pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps); + pw.println(" Time when the latest updated value of the Max charging current was" + + " sent via battery changed broadcast: " + + TimeUtils.formatDuration(mLastBroadcastMaxChargingCurrentUpdateTime)); pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts); pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounterUah); pw.println(" status: " + mHealthInfo.batteryStatus); @@ -1624,7 +1667,8 @@ public final class BatteryService extends SystemService { pw.println(" scale: " + BATTERY_SCALE); pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts); pw.println(" Time when the latest updated value of the voltage was sent via " - + "battery changed broadcast: " + mLastBroadcastVoltageUpdateTime); + + "battery changed broadcast: " + + TimeUtils.formatDuration(mLastBroadcastVoltageUpdateTime)); pw.println(" The last voltage value sent via the battery changed broadcast: " + mLastBroadcastBatteryVoltage); pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius); diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index aabbd3bf49ae..36dff89d9d61 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -101,6 +101,9 @@ import java.util.Map; import java.util.concurrent.Executors; import java.util.stream.Collectors; +import com.android.server.pm.BackgroundInstallControlService; +import com.android.server.pm.BackgroundInstallControlCallbackHelper; + /** * @hide */ @@ -138,6 +141,10 @@ public class BinaryTransparencyService extends SystemService { static final int MBA_STATUS_NEW_INSTALL = 3; // used for indicating newly installed MBAs that are updated (but unused currently) static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4; + // used for indicating preloaded MBAs that are downgraded + static final int MBA_STATUS_DOWNGRADED_PRELOADED = 5; + // used for indicating MBAs that are uninstalled + static final int MBA_STATUS_UNINSTALLED = 6; @VisibleForTesting static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION = @@ -202,7 +209,9 @@ public class BinaryTransparencyService extends SystemService { * @param mbaStatus Assign this value of MBA status to the returned elements. * @return a @{@code List<IBinaryTransparencyService.AppInfo>} */ - private @NonNull List<IBinaryTransparencyService.AppInfo> collectAppInfo( + @VisibleForTesting + @NonNull + List<IBinaryTransparencyService.AppInfo> collectAppInfo( PackageState packageState, int mbaStatus) { // compute content digest if (DEBUG) { @@ -336,27 +345,28 @@ public class BinaryTransparencyService extends SystemService { + " packages after considering APEXs."); } - // proceed with all preloaded apps - List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo = - collectAllUpdatedPreloadInfo(packagesMeasured); - for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) { - packagesMeasured.putBoolean(appInfo.packageName, true); - writeAppInfoToLog(appInfo); - } - if (DEBUG) { - Slog.d(TAG, "Measured " + packagesMeasured.size() - + " packages after considering preloads"); - } - - if (!android.app.Flags.backgroundInstallControlCallbackApi() - && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) { - // lastly measure all newly installed MBAs - List<IBinaryTransparencyService.AppInfo> allMbaInfo = - collectAllSilentInstalledMbaInfo(packagesMeasured); - for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) { + if (!android.app.Flags.backgroundInstallControlCallbackApi()) { + // proceed with all preloaded apps + List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo = + collectAllUpdatedPreloadInfo(packagesMeasured); + for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) { packagesMeasured.putBoolean(appInfo.packageName, true); writeAppInfoToLog(appInfo); } + if (DEBUG) { + Slog.d(TAG, "Measured " + packagesMeasured.size() + + " packages after considering preloads"); + } + + if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) { + // lastly measure all newly installed MBAs + List<IBinaryTransparencyService.AppInfo> allMbaInfo = + collectAllSilentInstalledMbaInfo(packagesMeasured); + for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) { + packagesMeasured.putBoolean(appInfo.packageName, true); + writeAppInfoToLog(appInfo); + } + } } long timeSpentMeasuring = System.currentTimeMillis() - currentTimeMs; digestAllPackagesLatency.logSample(timeSpentMeasuring); @@ -466,7 +476,8 @@ public class BinaryTransparencyService extends SystemService { apexInfo.signerDigests); } - private void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) { + @VisibleForTesting + void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) { // Must order by the proto's field number. FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED, appInfo.packageName, @@ -1165,41 +1176,86 @@ public class BinaryTransparencyService extends SystemService { * TODO: Add a host test for testing registration and callback of BicCallbackHandler * b/380002484 */ + @VisibleForTesting static class BicCallbackHandler extends IRemoteCallback.Stub { - private static final String BIC_CALLBACK_HANDLER_TAG = - "BTS.BicCallbackHandler"; - private final BinaryTransparencyServiceImpl mServiceImpl; - static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; + private static final String BIC_CALLBACK_HANDLER_TAG = TAG + ".BicCallbackHandler"; - BicCallbackHandler(BinaryTransparencyServiceImpl impl) { - mServiceImpl = impl; + private static final int INSTALL_EVENT_TYPE_UNSET = -1; + + private final IBicAppInfoHelper mBicAppInfoHelper; + + @VisibleForTesting + BicCallbackHandler(IBicAppInfoHelper bicAppInfoHelper) { + mBicAppInfoHelper = bicAppInfoHelper; } @Override public void sendResult(Bundle data) { - String packageName = data.getString(FLAGGED_PACKAGE_NAME_KEY); - if (packageName == null) return; - if (DEBUG) { - Slog.d(BIC_CALLBACK_HANDLER_TAG, "background install event detected for " - + packageName); - } - - PackageState packageState = LocalServices.getService(PackageManagerInternal.class) - .getPackageStateInternal(packageName); - if (packageState == null) { - Slog.w(TAG, "Package state is unavailable, ignoring the package " - + packageName); - return; - } - if (packageState.isUpdatedSystemApp()) { + String packageName = data.getString( + BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY); + int installType = data.getInt( + BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY, + INSTALL_EVENT_TYPE_UNSET); + if (packageName == null || installType == INSTALL_EVENT_TYPE_UNSET) { + Slog.w(BIC_CALLBACK_HANDLER_TAG, "Package name or install type is " + + "unavailable, ignoring event"); return; } - List<IBinaryTransparencyService.AppInfo> mbaInfo = mServiceImpl.collectAppInfo( - packageState, MBA_STATUS_NEW_INSTALL); - for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) { - mServiceImpl.writeAppInfoToLog(appInfo); + Slog.d(BIC_CALLBACK_HANDLER_TAG, "Detected new bic event for: " + packageName); + if (installType == BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL) { + PackageState packageState = LocalServices.getService(PackageManagerInternal.class) + .getPackageStateInternal(packageName); + if (packageState == null) { + Slog.w(TAG, "Package state is unavailable, ignoring the package " + + packageName); + return; + } + int mbaStatus = MBA_STATUS_NEW_INSTALL; + if (packageState.isUpdatedSystemApp()) { + mbaStatus = MBA_STATUS_UPDATED_PRELOAD; + } + List<IBinaryTransparencyService.AppInfo> mbaInfo = mBicAppInfoHelper.collectAppInfo( + packageState, mbaStatus); + for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) { + mBicAppInfoHelper.writeAppInfoToLog(appInfo); + } + } else if (installType + == BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL) { + IBinaryTransparencyService.AppInfo appInfo + = new IBinaryTransparencyService.AppInfo(); + // since app is already uninstalled we won't be able to retrieve additional + // info on it. + appInfo.packageName = packageName; + appInfo.mbaStatus = MBA_STATUS_UNINSTALLED; + mBicAppInfoHelper.writeAppInfoToLog(appInfo); + } else { + Slog.w(BIC_CALLBACK_HANDLER_TAG, "Unsupported BIC event: " + installType); } } + + /** + * A wrapper of interface for{@link FrameworkStatsLog and ApkDigests} + * for easier testing + */ + @VisibleForTesting + public interface IBicAppInfoHelper { + + /** + * A wrapper of {@link FrameworkStatsLog} + * + * @param appInfo The app info of the changed MBA to be logged + */ + public void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo); + + /** + * A wrapper of {@link BinaryTransparencyServiceImpl} + * + * @param packageState The packageState provided retrieved from PackageManagerInternal + * @param mbaStatus The MBA status of the package + */ + public List<IBinaryTransparencyService.AppInfo> collectAppInfo( + PackageState packageState, int mbaStatus); + } }; /** @@ -1580,13 +1636,37 @@ public class BinaryTransparencyService extends SystemService { } private void registerBicCallback() { + if(!com.android.server.flags.Flags.optionalBackgroundInstallControl()) { + Slog.d(TAG, "BICS is disabled for this device, skipping registration."); + return; + } IBackgroundInstallControlService iBics = IBackgroundInstallControlService.Stub.asInterface( ServiceManager.getService( Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); + if(iBics == null) { + Slog.e(TAG, "Failed to register BackgroundInstallControl callback, either " + + "background install control service does not exist or disabled on this " + + "build."); + return; + } try { iBics.registerBackgroundInstallCallback( - new BicCallbackHandler(mServiceImpl)); + new BicCallbackHandler( + new BicCallbackHandler.IBicAppInfoHelper() { + @Override + public void writeAppInfoToLog( + IBinaryTransparencyService.AppInfo appInfo) { + mServiceImpl.writeAppInfoToLog(appInfo); + } + + @Override + public List<IBinaryTransparencyService.AppInfo> collectAppInfo( + PackageState packageState, int mbaStatus) { + return mServiceImpl.collectAppInfo(packageState, mbaStatus); + } + } + )); } catch (RemoteException e) { Slog.e(TAG, "Failed to register BackgroundInstallControl callback."); } @@ -1633,8 +1713,12 @@ public class BinaryTransparencyService extends SystemService { } String packageName = data.getSchemeSpecificPart(); - // now we've got to check what package is this - if (isPackagePreloaded(packageName) || isPackageAnApex(packageName)) { + + boolean shouldMeasureMba = + !android.app.Flags.backgroundInstallControlCallbackApi() + && isPackagePreloaded(packageName); + + if (shouldMeasureMba || isPackageAnApex(packageName)) { Slog.d(TAG, packageName + " was updated. Scheduling measurement..."); UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext, BinaryTransparencyService.this); diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index ce66dc3c76cb..8da835896bd3 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -176,6 +176,10 @@ "include-filter": "com.android.server.wm.BackgroundActivityStart*" } ] + }, + { + "name": "FrameworksMockingServicesTests_service_batteryServiceTest", + "file_patterns": ["BatteryService\\.java"] } ] } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d79e66db8661..01daceba5fb9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -131,6 +131,9 @@ import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; import static android.provider.Settings.Global.DEBUG_APP; import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; import static android.security.Flags.preventIntentRedirect; +import static android.security.Flags.preventIntentRedirectCollectNestedKeysOnServerIfNotCollected; +import static android.security.Flags.preventIntentRedirectShowToastIfNestedKeysNotCollected; +import static android.security.Flags.preventIntentRedirectThrowExceptionIfNestedKeysNotCollected; import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS; import static android.view.Display.INVALID_DISPLAY; @@ -387,6 +390,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.autofill.AutofillManagerInternal; +import android.widget.Toast; import com.android.internal.annotations.CompositeRWLock; import com.android.internal.annotations.GuardedBy; @@ -437,6 +441,7 @@ import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.ThreadPriorityBooster; +import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.appop.AppOpsService; @@ -478,6 +483,7 @@ import com.android.server.wm.WindowProcessController; import dalvik.annotation.optimization.NeverCompile; import dalvik.system.VMRuntime; + import libcore.util.EmptyArray; import java.io.File; @@ -19313,9 +19319,32 @@ public class ActivityManagerService extends IActivityManager.Stub */ public void addCreatorToken(@Nullable Intent intent, String creatorPackage) { if (!preventIntentRedirect()) return; - if (intent == null) return; + if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0) { + Slog.wtf(TAG, + "[IntentRedirect] The intent does not have its nested keys collected as a " + + "preparation for creating intent creator tokens. Intent: " + + intent + "; creatorPackage: " + creatorPackage); + if (preventIntentRedirectShowToastIfNestedKeysNotCollected()) { + UiThread.getHandler().post( + () -> Toast.makeText(mContext, + "Nested keys not collected. go/report-bug-intentRedir to report a" + + " bug", Toast.LENGTH_LONG).show()); + } + if (preventIntentRedirectThrowExceptionIfNestedKeysNotCollected()) { + // this flag will be internal only, not ramped to public. + throw new SecurityException( + "The intent does not have its nested keys collected as a preparation for " + + "creating intent creator tokens. Intent: " + + intent + "; creatorPackage: " + creatorPackage); + } + if (preventIntentRedirectCollectNestedKeysOnServerIfNotCollected()) { + // this flag will be ramped to public. + intent.collectExtraIntentKeys(); + } + } + String targetPackage = intent.getComponent() != null ? intent.getComponent().getPackageName() : intent.getPackage(); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index fa40283d43e1..37d058b06b99 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -857,6 +857,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } options.setDismissKeyguardIfInsecure(); } + intent.collectExtraIntentKeys(); if (mWaitOption) { result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent, mimeType, null, null, 0, mStartFlags, profilerInfo, @@ -975,6 +976,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } pw.println("Starting service: " + intent); pw.flush(); + intent.collectExtraIntentKeys(); ComponentName cn = mInterface.startService(null, intent, intent.getType(), asForeground, SHELL_PACKAGE_NAME, null, mUserId); if (cn == null) { @@ -1007,6 +1009,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } pw.println("Stopping service: " + intent); pw.flush(); + intent.collectExtraIntentKeys(); int result = mInterface.stopService(null, intent, intent.getType(), mUserId); if (result == 0) { err.println("Service not stopped: was not running."); @@ -1404,6 +1407,12 @@ final class ActivityManagerShellCommand extends ShellCommand { heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof"; } + String argAfterHeapFile = getNextArg(); + if (argAfterHeapFile != null) { + err.println("Error: Arguments cannot be placed after the heap file"); + return -1; + } + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(heapFile, "w"); if (fd == null) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index d5bd05764f38..400ebfde1741 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1126,20 +1126,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub break; } case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL: - if (Flags.disableCompositeBatteryUsageStatsAtoms()) { - return StatsManager.PULL_SKIP; - } + return StatsManager.PULL_SKIP; - final BatteryUsageStatsQuery queryPowerProfile = - new BatteryUsageStatsQuery.Builder() - .setMaxStatsAgeMs(0) - .includeProcessStateData() - .includeVirtualUids() - .powerProfileModeledOnly() - .includePowerModels() - .build(); - bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0); - break; case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET: { if (Flags.disableCompositeBatteryUsageStatsAtoms()) { return StatsManager.PULL_SKIP; @@ -3184,7 +3172,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } - private void dumpUsageStats(FileDescriptor fd, PrintWriter pw, int model, + private void dumpUsageStats(FileDescriptor fd, PrintWriter pw, boolean proto, boolean accumulated) { awaitCompletion(); syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL); @@ -3196,9 +3184,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub if (Flags.batteryUsageStatsByPowerAndScreenState()) { builder.includeScreenStateData().includePowerStateData(); } - if (model == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { - builder.powerProfileModeledOnly(); - } if (accumulated) { builder.accumulated(); } @@ -3393,7 +3378,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub dumpPowerProfile(pw); return; } else if ("--usage".equals(arg)) { - int model = BatteryConsumer.POWER_MODEL_UNDEFINED; boolean proto = false; boolean accumulated = false; for (int j = i + 1; j < args.length; j++) { @@ -3401,29 +3385,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub case "--proto": proto = true; break; - case "--model": { - if (j + 1 < args.length) { - j++; - if ("power-profile".equals(args[j])) { - model = BatteryConsumer.POWER_MODEL_POWER_PROFILE; - } else { - pw.println("Unknown power model: " + args[j]); - dumpHelp(pw); - return; - } - } else { - pw.println("--model without a value"); - dumpHelp(pw); - return; - } - break; - } case "--accumulated": accumulated = true; break; } } - dumpUsageStats(fd, pw, model, proto, accumulated); + dumpUsageStats(fd, pw, proto, accumulated); return; } else if ("--wakeups".equals(arg)) { mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "), diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java index c6cb67f4efa8..354f281551b2 100644 --- a/services/core/java/com/android/server/am/BroadcastController.java +++ b/services/core/java/com/android/server/am/BroadcastController.java @@ -57,6 +57,7 @@ import android.app.ApplicationExitInfo; import android.app.ApplicationThreadConstants; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; +import android.app.BroadcastStickyCache; import android.app.IApplicationThread; import android.app.compat.CompatChanges; import android.appwidget.AppWidgetManager; @@ -183,6 +184,13 @@ class BroadcastController { final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>(); /** + * If {@code false} invalidate the list of {@link android.os.IpcDataCache} present inside the + * {@link BroadcastStickyCache} class. + * The invalidation is required to start caching of the sticky broadcast in the client side. + */ + private volatile boolean mAreStickyCachesInvalidated = false; + + /** * Resolver for broadcast intents to registered receivers. * Holds BroadcastFilter (subclass of IntentFilter). */ @@ -288,6 +296,11 @@ class BroadcastController { IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) { mService.enforceNotIsolatedCaller("registerReceiver"); + + if (!mAreStickyCachesInvalidated) { + BroadcastStickyCache.invalidateAllCaches(); + mAreStickyCachesInvalidated = true; + } ArrayList<StickyBroadcast> stickyBroadcasts = null; ProcessRecord callerApp = null; final boolean visibleToInstantApps = @@ -700,6 +713,7 @@ class BroadcastController { String[] excludedPackages, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { mService.enforceNotIsolatedCaller("broadcastIntent"); + final int result; synchronized (mService) { intent = verifyBroadcastLocked(intent); @@ -722,7 +736,7 @@ class BroadcastController { final long origId = Binder.clearCallingIdentity(); try { - return broadcastIntentLocked(callerApp, + result = broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, callingFeatureId, intent, resolvedType, resultToApp, resultTo, resultCode, resultData, resultExtras, requiredPermissions, excludedPermissions, excludedPackages, @@ -733,6 +747,11 @@ class BroadcastController { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } } + + if (sticky && result == ActivityManager.BROADCAST_SUCCESS) { + BroadcastStickyCache.invalidateCache(intent.getAction()); + } + return result; } // Not the binder call surface @@ -743,6 +762,7 @@ class BroadcastController { boolean serialized, boolean sticky, int userId, BackgroundStartPrivileges backgroundStartPrivileges, @Nullable int[] broadcastAllowList) { + final int result; synchronized (mService) { intent = verifyBroadcastLocked(intent); @@ -750,7 +770,7 @@ class BroadcastController { String[] requiredPermissions = requiredPermission == null ? null : new String[] {requiredPermission}; try { - return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType, + result = broadcastIntentLocked(null, packageName, featureId, intent, resolvedType, resultToApp, resultTo, resultCode, resultData, resultExtras, requiredPermissions, null, null, OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid, realCallingPid, userId, @@ -760,6 +780,11 @@ class BroadcastController { Binder.restoreCallingIdentity(origId); } } + + if (sticky && result == ActivityManager.BROADCAST_SUCCESS) { + BroadcastStickyCache.invalidateCache(intent.getAction()); + } + return result; } @GuardedBy("mService") @@ -1458,6 +1483,7 @@ class BroadcastController { list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid, callerAppProcessState, resolvedType)); } + BroadcastStickyCache.invalidateCache(intent.getAction()); } } @@ -1724,6 +1750,7 @@ class BroadcastController { Slog.w(TAG, msg); throw new SecurityException(msg); } + final ArrayList<String> changedStickyBroadcasts = new ArrayList<>(); synchronized (mStickyBroadcasts) { ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId); if (stickies != null) { @@ -1740,12 +1767,16 @@ class BroadcastController { if (list.size() <= 0) { stickies.remove(intent.getAction()); } + changedStickyBroadcasts.add(intent.getAction()); } if (stickies.size() <= 0) { mStickyBroadcasts.remove(userId); } } } + for (int i = changedStickyBroadcasts.size() - 1; i >= 0; --i) { + BroadcastStickyCache.invalidateCache(changedStickyBroadcasts.get(i)); + } } void finishReceiver(IBinder caller, int resultCode, String resultData, @@ -2124,9 +2155,18 @@ class BroadcastController { } void removeStickyBroadcasts(int userId) { + final ArrayList<String> changedStickyBroadcasts = new ArrayList<>(); synchronized (mStickyBroadcasts) { + final ArrayMap<String, ArrayList<StickyBroadcast>> stickies = + mStickyBroadcasts.get(userId); + if (stickies != null) { + changedStickyBroadcasts.addAll(stickies.keySet()); + } mStickyBroadcasts.remove(userId); } + for (int i = changedStickyBroadcasts.size() - 1; i >= 0; --i) { + BroadcastStickyCache.invalidateCache(changedStickyBroadcasts.get(i)); + } } @NeverCompile diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index f28f3e1e4fdf..7660c154efd5 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -140,6 +140,7 @@ public class SettingsToPropertiesMapper { // The list is sorted. @VisibleForTesting static final String[] sDeviceConfigAconfigScopes = new String[] { + "aaos_audio_triage", "aaos_power_triage", "aaos_sdv", "accessibility", @@ -534,9 +535,8 @@ public class SettingsToPropertiesMapper { * @param packageName the package of the flag * @param flagName the name of the flag * @param immediate if true, clear immediately; otherwise, clear on next reboot - * - * @hide */ + @VisibleForTesting public static void writeFlagOverrideRemovalRequest( ProtoOutputStream proto, String packageName, String flagName, boolean immediate) { long msgsToken = proto.start(StorageRequestMessages.MSGS); @@ -592,7 +592,8 @@ public class SettingsToPropertiesMapper { * apply flag local override in aconfig new storage * @param props */ - static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) { + @VisibleForTesting + public static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) { int num_requests = 0; ProtoOutputStream requests = new ProtoOutputStream(); for (String flagName : props.getKeyset()) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 5c2eb5cf1086..c6317bd29824 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -4954,31 +4954,24 @@ public class AudioService extends IAudioService.Stub } final Set<Integer> deviceTypes = getDeviceSetForStreamDirect(streamType); + Set<Integer> absVolumeDeviceTypes = new ArraySet<>( + AudioSystem.DEVICE_OUT_ALL_A2DP_SET); + absVolumeDeviceTypes.addAll(mAbsVolumeMultiModeCaseDevices); - final Set<Integer> a2dpDevices = AudioSystem.intersectionAudioDeviceTypes( - AudioSystem.DEVICE_OUT_ALL_A2DP_SET, deviceTypes); - if (!a2dpDevices.isEmpty()) { - int index = getStreamVolume(streamType, - a2dpDevices.toArray(new Integer[0])[0].intValue()); - mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index); - } - - final Set<Integer> absVolumeMultiModeCaseDevices = - AudioSystem.intersectionAudioDeviceTypes( - mAbsVolumeMultiModeCaseDevices, deviceTypes); - if (absVolumeMultiModeCaseDevices.isEmpty()) { + final Set<Integer> absVolumeDevices = + AudioSystem.intersectionAudioDeviceTypes(absVolumeDeviceTypes, deviceTypes); + if (absVolumeDevices.isEmpty()) { return; } - if (absVolumeMultiModeCaseDevices.size() > 1) { + if (absVolumeDevices.size() > 1) { Log.w(TAG, "onUpdateContextualVolumes too many active devices: " - + absVolumeMultiModeCaseDevices.stream().map(AudioSystem::getOutputDeviceName) + + absVolumeDevices.stream().map(AudioSystem::getOutputDeviceName) .collect(Collectors.joining(",")) + ", for stream: " + streamType); return; } - final int device = absVolumeMultiModeCaseDevices.toArray(new Integer[0])[0].intValue(); - + final int device = absVolumeDevices.toArray(new Integer[0])[0].intValue(); final int index = getStreamVolume(streamType, device); if (DEBUG_VOL) { @@ -4992,6 +4985,8 @@ public class AudioService extends IAudioService.Stub getVssForStreamOrDefault(streamType).getMaxIndex(), streamType); } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); + } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) { + mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index); } else { return; } diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index a2200c9f8bf5..1c01fb9f19e0 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -17,7 +17,7 @@ package com.android.server.audio; import static android.media.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE; -import static android.media.AudioPlaybackConfiguration.MUTED_BY_APP_OPS; +import static android.media.AudioPlaybackConfiguration.MUTED_BY_OP_PLAY_AUDIO; import static android.media.AudioPlaybackConfiguration.MUTED_BY_CLIENT_VOLUME; import static android.media.AudioPlaybackConfiguration.MUTED_BY_MASTER; import static android.media.AudioPlaybackConfiguration.MUTED_BY_PORT_VOLUME; @@ -1377,8 +1377,8 @@ public final class PlaybackActivityMonitor if ((eventValue & MUTED_BY_STREAM_MUTED) != 0) { builder.append("streamMute "); } - if ((eventValue & MUTED_BY_APP_OPS) != 0) { - builder.append("appOps "); + if ((eventValue & MUTED_BY_OP_PLAY_AUDIO) != 0) { + builder.append("opPlayAudio "); } if ((eventValue & MUTED_BY_CLIENT_VOLUME) != 0) { builder.append("clientVolume "); diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig index d3da8dd2cfda..95ba69580bba 100644 --- a/services/core/java/com/android/server/biometrics/biometrics.aconfig +++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig @@ -34,3 +34,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "frr_dialog_improvement" + namespace: "biometrics_framework" + description: "This flag controls FRR dialog improvement" + bug: "380800403" +} diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 8b9c664a31fd..251344395ae3 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -19,7 +19,16 @@ package com.android.server.devicestate; import static android.Manifest.permission.CONTROL_DEVICE_STATE; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; +import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN; +import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN; +import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_DUAL_DISPLAY; +import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_REAR_DISPLAY; +import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST; import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS; @@ -44,6 +53,10 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.IProcessObserver; import android.content.Context; +import android.frameworks.devicestate.DeviceStateConfiguration; +import android.frameworks.devicestate.ErrorCode; +import android.frameworks.devicestate.IDeviceStateListener; +import android.frameworks.devicestate.IDeviceStateService; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateInfo; import android.hardware.devicestate.DeviceStateManager; @@ -56,9 +69,12 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.os.ShellCallback; import android.os.SystemProperties; import android.os.Trace; +import android.util.LongSparseLongArray; import android.util.Slog; import android.util.SparseArray; @@ -82,6 +98,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.WeakHashMap; @@ -130,6 +147,8 @@ public final class DeviceStateManagerService extends SystemService { @NonNull private final BinderService mBinderService; @NonNull + private final HalService mHalService; + @NonNull private final OverrideRequestController mOverrideRequestController; @NonNull private final DeviceStateProviderListener mDeviceStateProviderListener; @@ -139,7 +158,7 @@ public final class DeviceStateManagerService extends SystemService { // All supported device states keyed by identifier. @GuardedBy("mLock") - private SparseArray<DeviceState> mDeviceStates = new SparseArray<>(); + private final SparseArray<DeviceState> mDeviceStates = new SparseArray<>(); // The current committed device state. Will be empty until the first device state provided by // the DeviceStateProvider is committed. @@ -177,7 +196,7 @@ public final class DeviceStateManagerService extends SystemService { @GuardedBy("mLock") private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); - private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>(); + private final Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>(); private Set<Integer> mFoldedDeviceStates = new HashSet<>(); @@ -259,6 +278,7 @@ public final class DeviceStateManagerService extends SystemService { mDeviceStateProviderListener = new DeviceStateProviderListener(); mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener); mBinderService = new BinderService(); + mHalService = new HalService(); mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mDeviceStateNotificationController = new DeviceStateNotificationController( context, mHandler, @@ -272,6 +292,10 @@ public final class DeviceStateManagerService extends SystemService { @Override public void onStart() { publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService); + String halServiceName = IDeviceStateService.DESCRIPTOR + "/default"; + if (ServiceManager.isDeclared(halServiceName)) { + publishBinderService(halServiceName, mHalService); + } publishLocalService(DeviceStateManagerInternal.class, new LocalService()); if (!Flags.deviceStatePropertyMigration()) { @@ -440,6 +464,11 @@ public final class DeviceStateManagerService extends SystemService { return mBinderService; } + @VisibleForTesting + IDeviceStateService getHalBinderService() { + return mHalService; + } + private void updateSupportedStates(DeviceState[] supportedDeviceStates, @DeviceStateProvider.SupportedStatesUpdatedReason int reason) { synchronized (mLock) { @@ -1282,6 +1311,124 @@ public final class DeviceStateManagerService extends SystemService { } } + private final class HalService extends IDeviceStateService.Stub { + private final LongSparseLongArray mPublicProperties = new LongSparseLongArray(); + public HalService() { + mPublicProperties.put( + DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED, + FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED); + mPublicProperties.put( + DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN, + FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN); + mPublicProperties.put( + DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN, + FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN); + mPublicProperties.put( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY); + mPublicProperties.put( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, + FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY); + mPublicProperties.put( + PROPERTY_FEATURE_REAR_DISPLAY, + FEATURE_REAR_DISPLAY); + mPublicProperties.put( + PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT, + FEATURE_DUAL_DISPLAY); + } + + private final class HalBinderCallback implements IDeviceStateManagerCallback { + private final IDeviceStateListener mListener; + + private HalBinderCallback(@NonNull IDeviceStateListener listener) { + mListener = listener; + } + + @Override + public void onDeviceStateInfoChanged(DeviceStateInfo info) throws RemoteException { + DeviceStateConfiguration config = new DeviceStateConfiguration(); + Set<Integer> systemProperties = new HashSet<>( + info.currentState.getConfiguration().getSystemProperties()); + Set<Integer> physicalProperties = new HashSet<>( + info.currentState.getConfiguration().getPhysicalProperties()); + config.deviceProperties = 0; + for (Integer prop : systemProperties) { + Long publicProperty = mPublicProperties.get(prop); + if (publicProperty != null) { + config.deviceProperties |= publicProperty.longValue(); + } + } + for (Integer prop : physicalProperties) { + Long publicProperty = mPublicProperties.get(prop); + if (publicProperty != null) { + config.deviceProperties |= publicProperty.longValue(); + } + } + mListener.onDeviceStateChanged(config); + } + + @Override + public void onRequestActive(IBinder token) { + //No-op + } + + @Override + public void onRequestCanceled(IBinder token) { + //No-op + } + + @Override + public IBinder asBinder() { + return mListener.asBinder(); + } + } + + @Override + public void registerListener(IDeviceStateListener listener) throws RemoteException { + if (listener == null) { + throw new ServiceSpecificException(ErrorCode.BAD_INPUT); + } + + final int callingPid = Binder.getCallingPid(); + final long token = Binder.clearCallingIdentity(); + try { + HalBinderCallback callback = new HalBinderCallback(listener); + DeviceStateInfo info = registerProcess(callingPid, callback); + if (info != null) { + callback.onDeviceStateInfoChanged(info); + } + } catch (SecurityException e) { + throw new ServiceSpecificException(ErrorCode.ALREADY_EXISTS); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void unregisterListener(IDeviceStateListener listener) throws RemoteException { + final int callingPid = Binder.getCallingPid(); + + synchronized (mLock) { + if (mProcessRecords.contains(callingPid)) { + mProcessRecords.remove(callingPid); + return; + } + } + + throw new ServiceSpecificException(ErrorCode.BAD_INPUT); + } + + @Override + public int getInterfaceVersion() throws RemoteException { + return IDeviceStateService.VERSION; + } + + @Override + public String getInterfaceHash() throws RemoteException { + return IDeviceStateService.HASH; + } + } + /** Implementation of {@link IDeviceStateManager} published as a binder service. */ private final class BinderService extends IDeviceStateManager.Stub { diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index e273c6862fe0..45106f54cb9f 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -257,6 +257,11 @@ public class DisplayManagerFlags { Flags::displayListenerPerformanceImprovements ); + private final FlagState mSubscribeGranularDisplayEvents = new FlagState( + Flags.FLAG_SUBSCRIBE_GRANULAR_DISPLAY_EVENTS, + Flags::subscribeGranularDisplayEvents + ); + /** * @return {@code true} if 'port' is allowed in display layout configuration file. */ @@ -552,6 +557,13 @@ public class DisplayManagerFlags { } /** + * @return {@code true} if the flag for subscribing to granular display events is enabled + */ + public boolean isSubscribeGranularDisplayEventsEnabled() { + return mSubscribeGranularDisplayEvents.isEnabled(); + } + + /** * dumps all flagstates * @param pw printWriter */ @@ -605,6 +617,7 @@ public class DisplayManagerFlags { pw.println(" " + mGetSupportedRefreshRatesFlagState); pw.println(" " + mEnablePluginManagerFlagState); pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState); + pw.println(" " + mSubscribeGranularDisplayEvents); } private static class FlagState { diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index e7ea868ca04f..3976d01d806d 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -478,3 +478,14 @@ flag { bug: "378385869" is_fixed_read_only: true } + +flag { + name: "subscribe_granular_display_events" + namespace: "display_manager" + description: "Enable subscription to granular display change events." + bug: "379250634" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java index 53c02176f11d..2e66fbc16496 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java @@ -16,6 +16,8 @@ package com.android.server.hdmi; +import static android.media.tv.flags.Flags.hdmiControlCollectPhysicalAddress; + import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_ARC_PENDING; import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_CONNECTED; import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_PENDING; @@ -35,6 +37,8 @@ public class HdmiCecAtomWriter { @VisibleForTesting protected static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100; private static final int ERROR_CODE_UNKNOWN = -1; + @VisibleForTesting + protected static final int PHYSICAL_ADDRESS_INVALID = 0xFFFF; /** * Writes a HdmiCecMessageReported atom representing an HDMI CEC message. @@ -95,6 +99,11 @@ public class HdmiCecAtomWriter { return createUserControlPressedSpecialArgs(message); case Constants.MESSAGE_FEATURE_ABORT: return createFeatureAbortSpecialArgs(message); + case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS: + if (hdmiControlCollectPhysicalAddress()) { + return createReportPhysicalAddressSpecialArgs(message); + } + return new MessageReportedSpecialArgs(); default: return new MessageReportedSpecialArgs(); } @@ -140,6 +149,23 @@ public class HdmiCecAtomWriter { } /** + * Constructs the special arguments for a <Report Physical Address> message. + * + * @param message The HDMI CEC message to log + */ + private MessageReportedSpecialArgs createReportPhysicalAddressSpecialArgs( + HdmiCecMessage message) { + MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); + + if (message.getParams().length > 1) { + int physicalAddress = (message.getParams()[0] << 8) | message.getParams()[1]; + specialArgs.mPhysicalAddress = physicalAddress; + } + + return specialArgs; + } + + /** * Writes a HdmiCecMessageReported atom. * * @param genericArgs Generic arguments; shared by all HdmiCecMessageReported atoms @@ -156,7 +182,8 @@ public class HdmiCecAtomWriter { genericArgs.mSendMessageResult, specialArgs.mUserControlPressedCommand, specialArgs.mFeatureAbortOpcode, - specialArgs.mFeatureAbortReason); + specialArgs.mFeatureAbortReason, + specialArgs.mPhysicalAddress); } /** @@ -166,7 +193,7 @@ public class HdmiCecAtomWriter { protected void writeHdmiCecMessageReportedAtom(int uid, int direction, int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode, - int featureAbortReason) { + int featureAbortReason, int physicalAddress) { FrameworkStatsLog.write( FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED, uid, @@ -177,7 +204,8 @@ public class HdmiCecAtomWriter { sendMessageResult, userControlPressedCommand, featureAbortOpcode, - featureAbortReason); + featureAbortReason, + physicalAddress); } /** @@ -284,5 +312,6 @@ public class HdmiCecAtomWriter { int mUserControlPressedCommand = HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN; int mFeatureAbortOpcode = FEATURE_ABORT_OPCODE_UNKNOWN; int mFeatureAbortReason = HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN; + int mPhysicalAddress = PHYSICAL_ADDRESS_INVALID; } } diff --git a/services/core/java/com/android/server/incident/PendingReports.java b/services/core/java/com/android/server/incident/PendingReports.java index 35b3673fdf77..9a6c87e525e0 100644 --- a/services/core/java/com/android/server/incident/PendingReports.java +++ b/services/core/java/com/android/server/incident/PendingReports.java @@ -324,16 +324,12 @@ class PendingReports { // Allow system apps to skip the consent dialog and use their in-built consent mechanism // instead. - boolean captureConsentlessBugreportDelegatedConsentGranted = false; - if ((flags & IncidentManager.FLAG_ALLOW_CONSENTLESS_BUGREPORT) != 0) { - captureConsentlessBugreportDelegatedConsentGranted = - mPermissionManager.checkPermissionForDataDelivery( - Manifest.permission - .CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT, - attributionSource, - /* message= */ null) - == PERMISSION_GRANTED; - } + boolean captureConsentlessBugreportDelegatedConsentGranted = + mPermissionManager.checkPermissionForDataDelivery( + Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT, + attributionSource, + /* message= */ null) + == PERMISSION_GRANTED; if (captureConsentlessBugreportOnUserdebugBuildGranted || captureConsentlessBugreportDelegatedConsentGranted) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index b7af9a4b17bd..02dd884ad60d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -84,6 +84,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.res.Resources; +import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.InputManager; import android.inputmethodservice.InputMethodService; @@ -6888,6 +6889,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread @Override + public void setHandwritingTouchableRegion(Region region) { + synchronized (ImfLock.class) { + mImms.mHwController.setHandwritingTouchableRegion(region); + } + } + + @BinderThread + @Override public void createInputContentUriToken(Uri contentUri, String packageName, AndroidFuture future /* T=IBinder */) { @SuppressWarnings("unchecked") final AndroidFuture<IBinder> typedFuture = future; diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java index da31bf29a8e8..ccfa61b400b6 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java @@ -492,14 +492,21 @@ import java.util.concurrent.atomic.AtomicInteger; /* package */ void onTransactionResponse(int transactionId, boolean success) { TransactionAcceptConditions conditions = - transaction -> transaction.getTransactionId() == transactionId; + transaction -> { + if (transaction.getTransactionId() != transactionId) { + Log.w( + TAG, + "Unexpected transaction: expected " + + transactionId + + ", received " + + transaction.getTransactionId()); + return false; + } + return true; + }; ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions); if (transaction == null) { - Log.w(TAG, "Received unexpected transaction response (expected ID = " - + transactionId - + ", received ID = " - + transaction.getTransactionId() - + ")"); + Log.w(TAG, "Received unexpected transaction response"); return; } @@ -581,7 +588,7 @@ import java.util.concurrent.atomic.AtomicInteger; transaction.getTransactionType() == ContextHubTransaction.TYPE_QUERY_NANOAPPS; ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions); if (transaction == null) { - Log.w(TAG, "Received unexpected query response (expected " + transaction + ")"); + Log.w(TAG, "Received unexpected query response"); return; } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 27bc1cf3e631..2d2d2584edfd 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -221,6 +221,11 @@ class MediaRouter2ServiceImpl { } } + @NonNull + private SystemMediaRoute2Provider getSystemProviderForUser(@NonNull UserHandler userHandler) { + return userHandler.mSystemProvider; + } + // Start of methods that implement MediaRouter2 operations. @NonNull @@ -246,7 +251,7 @@ class MediaRouter2ServiceImpl { UserRecord userRecord = getOrCreateUserRecordLocked(userId); if (hasSystemRoutingPermissions) { MediaRoute2ProviderInfo providerInfo = - userRecord.mHandler.mSystemProvider.getProviderInfo(); + getSystemProviderForUser(userRecord.mHandler).getProviderInfo(); if (providerInfo != null) { systemRoutes = providerInfo.getRoutes(); } else { @@ -258,7 +263,8 @@ class MediaRouter2ServiceImpl { } } else { systemRoutes = new ArrayList<>(); - systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute()); + systemRoutes.add( + getSystemProviderForUser(userRecord.mHandler).getDefaultRoute()); } } return new ArrayList<>(systemRoutes); @@ -850,10 +856,11 @@ class MediaRouter2ServiceImpl { if (setDeviceRouteSelected) { // Return a fake system session that shows the device route as selected and // available bluetooth routes as transferable. - return userRecord.mHandler.mSystemProvider + return getSystemProviderForUser(userRecord.mHandler) .generateDeviceRouteSelectedSessionInfo(targetPackageName); } else { - sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos(); + sessionInfos = getSystemProviderForUser(userRecord.mHandler) + .getSessionInfos(); if (!sessionInfos.isEmpty()) { // Return a copy of the current system session with no modification, // except setting the client package name. @@ -866,7 +873,8 @@ class MediaRouter2ServiceImpl { } } else { return new RoutingSessionInfo.Builder( - userRecord.mHandler.mSystemProvider.getDefaultSessionInfo()) + getSystemProviderForUser(userRecord.mHandler) + .getDefaultSessionInfo()) .setClientPackageName(targetPackageName) .build(); } @@ -1374,7 +1382,7 @@ class MediaRouter2ServiceImpl { } manager.mLastSessionCreationRequest = null; } else { - String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId(); + String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId(); if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission() && !TextUtils.equals(route.getId(), defaultRouteId)) { @@ -1462,7 +1470,7 @@ class MediaRouter2ServiceImpl { routerRecord.mPackageName, routerRecord.mRouterId, route.getId())); UserHandler userHandler = routerRecord.mUserRecord.mHandler; - String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId(); + String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId(); if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission() && !TextUtils.equals(route.getId(), defaultRouteId)) { @@ -2125,11 +2133,12 @@ class MediaRouter2ServiceImpl { notifyRoutesUpdated(routesToReport.values().stream().toList()); List<RoutingSessionInfo> sessionInfos = - mUserRecord.mHandler.mSystemProvider.getSessionInfos(); + getSystemProviderForUser(mUserRecord.mHandler).getSessionInfos(); RoutingSessionInfo systemSessionToReport = newSystemRoutingPermissionValue && !sessionInfos.isEmpty() ? sessionInfos.get(0) - : mUserRecord.mHandler.mSystemProvider.getDefaultSessionInfo(); + : getSystemProviderForUser(mUserRecord.mHandler) + .getDefaultSessionInfo(); notifySessionInfoChanged(systemSessionToReport); } } @@ -2279,7 +2288,7 @@ class MediaRouter2ServiceImpl { if (route.isSystemRoute() && !hasSystemRoutingPermission()) { // The router lacks permission to modify system routing, so we hide system // route info from them. - route = mUserRecord.mHandler.mSystemProvider.getDefaultRoute(); + route = getSystemProviderForUser(mUserRecord.mHandler).getDefaultRoute(); } mRouter.requestCreateSessionByManager(uniqueRequestId, oldSession, route); } catch (RemoteException ex) { @@ -2535,6 +2544,10 @@ class MediaRouter2ServiceImpl { private boolean mRunning; + private SystemMediaRoute2Provider getSystemProvider() { + return mSystemProvider; + } + // TODO: (In Android S+) Pull out SystemMediaRoute2Provider out of UserHandler. UserHandler( @NonNull MediaRouter2ServiceImpl service, @@ -2544,21 +2557,24 @@ class MediaRouter2ServiceImpl { mServiceRef = new WeakReference<>(service); mUserRecord = userRecord; mSystemProvider = - new SystemMediaRoute2Provider( - service.mContext, UserHandle.of(userRecord.mUserId), looper); - mRouteProviders.add(mSystemProvider); + Flags.enableMirroringInMediaRouter2() + ? new SystemMediaRoute2Provider2( + service.mContext, UserHandle.of(userRecord.mUserId), looper) + : new SystemMediaRoute2Provider( + service.mContext, UserHandle.of(userRecord.mUserId), looper); + mRouteProviders.add(getSystemProvider()); mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, this, mUserRecord.mUserId); } void init() { - mSystemProvider.setCallback(this); + getSystemProvider().setCallback(this); } private void start() { if (!mRunning) { mRunning = true; - mSystemProvider.start(); + getSystemProvider().start(); mWatcher.start(); } } @@ -2567,7 +2583,7 @@ class MediaRouter2ServiceImpl { if (mRunning) { mRunning = false; mWatcher.stop(); // also stops all providers - mSystemProvider.stop(); + getSystemProvider().stop(); } } @@ -2659,7 +2675,7 @@ class MediaRouter2ServiceImpl { String indent = prefix + " "; pw.println(indent + "mRunning=" + mRunning); - mSystemProvider.dump(pw, prefix); + getSystemProvider().dump(pw, prefix); mWatcher.dump(pw, prefix); } @@ -2752,7 +2768,7 @@ class MediaRouter2ServiceImpl { hasAddedOrModifiedRoutes, hasRemovedRoutes, provider.mIsSystemRouteProvider, - mSystemProvider.getDefaultRoute()); + getSystemProvider().getDefaultRoute()); } private static String getPackageNameFromNullableRecord( @@ -2966,7 +2982,8 @@ class MediaRouter2ServiceImpl { } // Bypass checking router if it's the system session (routerRecord should be null) - if (TextUtils.equals(getProviderId(uniqueSessionId), mSystemProvider.getUniqueId())) { + if (TextUtils.equals( + getProviderId(uniqueSessionId), getSystemProvider().getUniqueId())) { return true; } @@ -3097,7 +3114,7 @@ class MediaRouter2ServiceImpl { && !matchingRequest.mRouterRecord.hasSystemRoutingPermission()) { // The router lacks permission to modify system routing, so we hide system routing // session info from them. - sessionInfo = mSystemProvider.getDefaultSessionInfo(); + sessionInfo = getSystemProvider().getDefaultSessionInfo(); } matchingRequest.mRouterRecord.notifySessionCreated( toOriginalRequestId(uniqueRequestId), sessionInfo); @@ -3111,13 +3128,13 @@ class MediaRouter2ServiceImpl { } // For system provider, notify all routers. - if (provider == mSystemProvider) { + if (provider == getSystemProvider()) { if (mServiceRef.get() == null) { return; } notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo); notifySessionInfoChangedToRouters( - getRouterRecords(false), mSystemProvider.getDefaultSessionInfo()); + getRouterRecords(false), getSystemProvider().getDefaultSessionInfo()); return; } @@ -3253,7 +3270,8 @@ class MediaRouter2ServiceImpl { MediaRoute2ProviderInfo systemProviderInfo = null; for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) { // TODO: Create MediaRoute2ProviderInfo#isSystemProvider() - if (TextUtils.equals(providerInfo.getUniqueId(), mSystemProvider.getUniqueId())) { + if (TextUtils.equals( + providerInfo.getUniqueId(), getSystemProvider().getUniqueId())) { // Adding routes from system provider will be handled below, so skip it here. systemProviderInfo = providerInfo; continue; @@ -3269,10 +3287,10 @@ class MediaRouter2ServiceImpl { // This shouldn't happen. Slog.wtf(TAG, "System route provider not found."); } - currentSystemSessionInfo = mSystemProvider.getSessionInfos().get(0); + currentSystemSessionInfo = getSystemProvider().getSessionInfos().get(0); } else { - currentRoutes.add(mSystemProvider.getDefaultRoute()); - currentSystemSessionInfo = mSystemProvider.getDefaultSessionInfo(); + currentRoutes.add(getSystemProvider().getDefaultRoute()); + currentSystemSessionInfo = getSystemProvider().getDefaultSessionInfo(); } if (!currentRoutes.isEmpty()) { diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java new file mode 100644 index 000000000000..a86e81881212 --- /dev/null +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.content.Context; +import android.media.MediaRoute2ProviderService; +import android.os.Looper; +import android.os.UserHandle; + +/** + * Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link + * MediaRoute2ProviderService provider services}. + * + * <p>System routes are those which can handle the system audio and/or video. + */ +/* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider { + SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) { + super(context, user, looper); + } +} diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java index c7e00d3cbb24..af329070ec22 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityService.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java @@ -25,7 +25,7 @@ import android.media.quality.IAmbientBacklightCallback; import android.media.quality.IMediaQualityManager; import android.media.quality.IPictureProfileCallback; import android.media.quality.ISoundProfileCallback; -import android.media.quality.MediaQualityContract; +import android.media.quality.MediaQualityContract.BaseParameters; import android.media.quality.ParamCapability; import android.media.quality.PictureProfile; import android.media.quality.PictureProfileHandle; @@ -42,6 +42,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; /** * This service manage picture profile and sound profile for TV setting. Also communicates with the @@ -75,10 +76,10 @@ public class MediaQualityService extends SystemService { SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); - values.put(MediaQualityContract.BaseParameters.PARAMETER_TYPE, pp.getProfileType()); - values.put(MediaQualityContract.BaseParameters.PARAMETER_NAME, pp.getName()); - values.put(MediaQualityContract.BaseParameters.PARAMETER_PACKAGE, pp.getPackageName()); - values.put(MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID, pp.getInputId()); + values.put(BaseParameters.PARAMETER_TYPE, pp.getProfileType()); + values.put(BaseParameters.PARAMETER_NAME, pp.getName()); + values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName()); + values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId()); values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters())); // id is auto-generated by SQLite upon successful insertion of row @@ -90,6 +91,7 @@ public class MediaQualityService extends SystemService { public void updatePictureProfile(String id, PictureProfile pp) { // TODO: implement } + @Override public void removePictureProfile(String id) { // TODO: implement @@ -99,8 +101,8 @@ public class MediaQualityService extends SystemService { public PictureProfile getPictureProfile(int type, String name) { SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase(); - String selection = MediaQualityContract.BaseParameters.PARAMETER_TYPE + " = ? AND " - + MediaQualityContract.BaseParameters.PARAMETER_NAME + " = ?"; + String selection = BaseParameters.PARAMETER_TYPE + " = ? AND " + + BaseParameters.PARAMETER_NAME + " = ?"; String[] selectionArguments = {Integer.toString(type), name}; try ( @@ -176,26 +178,26 @@ public class MediaQualityService extends SystemService { private String[] getAllPictureProfileColumns() { return new String[]{ - MediaQualityContract.BaseParameters.PARAMETER_ID, - MediaQualityContract.BaseParameters.PARAMETER_TYPE, - MediaQualityContract.BaseParameters.PARAMETER_NAME, - MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID, - MediaQualityContract.BaseParameters.PARAMETER_PACKAGE, + BaseParameters.PARAMETER_ID, + BaseParameters.PARAMETER_TYPE, + BaseParameters.PARAMETER_NAME, + BaseParameters.PARAMETER_INPUT_ID, + BaseParameters.PARAMETER_PACKAGE, mMediaQualityDbHelper.SETTINGS }; } private PictureProfile getPictureProfileFromCursor(Cursor cursor) { String returnId = cursor.getString(cursor.getColumnIndexOrThrow( - MediaQualityContract.BaseParameters.PARAMETER_ID)); + BaseParameters.PARAMETER_ID)); int type = cursor.getInt(cursor.getColumnIndexOrThrow( - MediaQualityContract.BaseParameters.PARAMETER_TYPE)); + BaseParameters.PARAMETER_TYPE)); String name = cursor.getString(cursor.getColumnIndexOrThrow( - MediaQualityContract.BaseParameters.PARAMETER_NAME)); + BaseParameters.PARAMETER_NAME)); String inputId = cursor.getString(cursor.getColumnIndexOrThrow( - MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID)); + BaseParameters.PARAMETER_INPUT_ID)); String packageName = cursor.getString(cursor.getColumnIndexOrThrow( - MediaQualityContract.BaseParameters.PARAMETER_PACKAGE)); + BaseParameters.PARAMETER_PACKAGE)); String settings = cursor.getString( cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS)); return new PictureProfile(returnId, type, name, inputId, @@ -204,7 +206,7 @@ public class MediaQualityService extends SystemService { @Override public List<PictureProfile> getPictureProfilesByPackage(String packageName) { - String selection = MediaQualityContract.BaseParameters.PARAMETER_PACKAGE + " = ?"; + String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; String[] selectionArguments = {packageName}; return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection, selectionArguments); @@ -217,14 +219,12 @@ public class MediaQualityService extends SystemService { @Override public List<String> getPictureProfilePackageNames() { - String [] column = {MediaQualityContract.BaseParameters.PARAMETER_NAME}; + String [] column = {BaseParameters.PARAMETER_NAME}; List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column, null, null); - List<String> packageNames = new ArrayList<>(); - for (PictureProfile pictureProfile: pictureProfiles) { - packageNames.add(pictureProfile.getName()); - } - return packageNames; + return pictureProfiles.stream() + .map(PictureProfile::getName) + .collect(Collectors.toList()); } private List<PictureProfile> getPictureProfilesBasedOnConditions(String[] columns, @@ -255,35 +255,135 @@ public class MediaQualityService extends SystemService { } @Override - public SoundProfile createSoundProfile(SoundProfile pp) { - // TODO: implement - return pp; + public SoundProfile createSoundProfile(SoundProfile sp) { + SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); + + ContentValues values = new ContentValues(); + values.put(BaseParameters.PARAMETER_NAME, sp.getName()); + values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName()); + values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId()); + values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(sp.getParameters())); + + long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values); + return new SoundProfile.Builder(sp).setProfileId(Long.toString(id)).build(); } + @Override - public void updateSoundProfile(String id, SoundProfile pp) { + public void updateSoundProfile(String id, SoundProfile sp) { // TODO: implement } + @Override public void removeSoundProfile(String id) { - // TODO: implement + SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); + String selection = BaseParameters.PARAMETER_ID + " = ?"; + String[] selectionArgs = {id}; + db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, selectionArgs); } + @Override public SoundProfile getSoundProfile(int type, String id) { - return null; + SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase(); + + String selection = BaseParameters.PARAMETER_ID + " = ?"; + String[] selectionArguments = {id}; + + try ( + Cursor cursor = db.query( + mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, + getAllSoundProfileColumns(), + selection, + selectionArguments, + /*groupBy=*/ null, + /*having=*/ null, + /*orderBy=*/ null) + ) { + int count = cursor.getCount(); + if (count == 0) { + return null; + } + if (count > 1) { + Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s" + + " in %s. Should only ever be 0 or 1.", count, id, + mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME)); + return null; + } + cursor.moveToFirst(); + return getSoundProfileFromCursor(cursor); + } } + @Override public List<SoundProfile> getSoundProfilesByPackage(String packageName) { - return new ArrayList<>(); + String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; + String[] selectionArguments = {packageName}; + return getSoundProfilesBasedOnConditions(getAllSoundProfileColumns(), selection, + selectionArguments); } + @Override public List<SoundProfile> getAvailableSoundProfiles() { return new ArrayList<>(); } + @Override public List<String> getSoundProfilePackageNames() { - return new ArrayList<>(); + String [] column = {BaseParameters.PARAMETER_NAME}; + List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column, + null, null); + return soundProfiles.stream() + .map(SoundProfile::getName) + .collect(Collectors.toList()); + } + + private String[] getAllSoundProfileColumns() { + return new String[]{ + BaseParameters.PARAMETER_ID, + BaseParameters.PARAMETER_NAME, + BaseParameters.PARAMETER_INPUT_ID, + BaseParameters.PARAMETER_PACKAGE, + mMediaQualityDbHelper.SETTINGS + }; + } + + private SoundProfile getSoundProfileFromCursor(Cursor cursor) { + String returnId = cursor.getString( + cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_ID)); + int type = cursor.getInt( + cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_TYPE)); + String name = cursor.getString( + cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_NAME)); + String inputId = cursor.getString( + cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_INPUT_ID)); + String packageName = cursor.getString( + cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_PACKAGE)); + String settings = cursor.getString( + cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS)); + return new SoundProfile(returnId, type, name, inputId, packageName, + jsonToBundle(settings)); } + private List<SoundProfile> getSoundProfilesBasedOnConditions(String[] columns, + String selection, String[] selectionArguments) { + SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase(); + + try ( + Cursor cursor = db.query( + mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, + columns, + selection, + selectionArguments, + /*groupBy=*/ null, + /*having=*/ null, + /*orderBy=*/ null) + ) { + List<SoundProfile> soundProfiles = new ArrayList<>(); + while (cursor.moveToNext()) { + soundProfiles.add(getSoundProfileFromCursor(cursor)); + } + return soundProfiles; + } + } @Override public void registerPictureProfileCallback(final IPictureProfileCallback callback) { diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java index 27c4e9dca586..bc0fc2b0b7d9 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java @@ -33,9 +33,10 @@ import com.android.server.ServiceThread; public class BackgroundInstallControlCallbackHelper { - @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; - @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId"; - @VisibleForTesting static final String INSTALL_EVENT_TYPE_KEY = "installEventType"; + public static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; + public static final String FLAGGED_USER_ID_KEY = "userId"; + public static final String INSTALL_EVENT_TYPE_KEY = "installEventType"; + private static final String TAG = "BackgroundInstallControlCallbackHelper"; private final Handler mHandler; diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java index 5bd2c994160c..42b8dd71c6e2 100644 --- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java +++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LI import static android.os.Process.SYSTEM_UID; import android.annotation.NonNull; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -35,6 +36,7 @@ import android.os.Handler; import android.os.OutcomeReceiver; import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArraySet; import android.util.Slog; @@ -52,6 +54,8 @@ import java.util.concurrent.TimeUnit; public class InstallDependencyHelper { private static final String TAG = InstallDependencyHelper.class.getSimpleName(); private static final boolean DEBUG = true; + private static final String ROLE_SYSTEM_DEPENDENCY_INSTALLER = + "android.app.role.SYSTEM_DEPENDENCY_INSTALLER"; // The maximum amount of time to wait before the system unbinds from the verifier. private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6); private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1); @@ -154,8 +158,20 @@ public class InstallDependencyHelper { } } + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + if (roleManager == null) { + Slog.w(TAG, "Cannot find RoleManager system service"); + return false; + } + List<String> holders = roleManager.getRoleHoldersAsUser( + ROLE_SYSTEM_DEPENDENCY_INSTALLER, UserHandle.of(userId)); + if (holders.isEmpty()) { + Slog.w(TAG, "No holders of ROLE_SYSTEM_DEPENDENCY_INSTALLER found"); + return false; + } + Intent serviceIntent = new Intent(ACTION_INSTALL_DEPENDENCY); - // TODO(b/372862145): Use RoleManager to find the package name + serviceIntent.setPackage(holders.getFirst()); List<ResolveInfo> resolvedIntents = snapshot.queryIntentServicesInternal( serviceIntent, /*resolvedType=*/ null, /*flags=*/0, userId, SYSTEM_UID, Process.INVALID_PID, @@ -165,7 +181,6 @@ public class InstallDependencyHelper { return false; } - ResolveInfo resolveInfo = resolvedIntents.getFirst(); ComponentName componentName = resolveInfo.getComponentInfo().getComponentName(); serviceIntent.setComponent(componentName); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 715633410575..040b1943b23d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5875,6 +5875,67 @@ public class PackageManagerService implements PackageSender, TestUtilityService userId, callingPackage); } + @Override + public void setPageSizeAppCompatFlagsSettingsOverride(String packageName, boolean enabled) { + final int callingUid = Binder.getCallingUid(); + final int callingAppId = UserHandle.getAppId(callingUid); + + if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) { + throw new SecurityException("Caller must be the system or root."); + } + + int settingsMode = enabled + ? ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED + : ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED; + PackageStateMutator.Result result = + commitPackageStateMutation( + null, + packageName, + packageState -> + packageState + .setPageSizeAppCompatFlags(settingsMode)); + if (result.isSpecificPackageNull()) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + scheduleWriteSettings(); + } + + @Override + public boolean isPageSizeCompatEnabled(String packageName) { + final int callingUid = Binder.getCallingUid(); + final int callingAppId = UserHandle.getAppId(callingUid); + final int userId = UserHandle.getCallingUserId(); + + if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) { + throw new SecurityException("Caller must be the system or root."); + } + + PackageStateInternal packageState = + snapshotComputer().getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId); + + return packageState == null ? false : packageState.isPageSizeAppCompatEnabled(); + } + + @Override + public String getPageSizeCompatWarningMessage(String packageName) { + final int callingUid = Binder.getCallingUid(); + final int callingAppId = UserHandle.getAppId(callingUid); + final int userId = UserHandle.getCallingUserId(); + + if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) { + throw new SecurityException("Caller must be the system or root."); + } + + PackageStateInternal packageState = + snapshotComputer().getPackageStateForInstalledAndFiltered( + packageName, callingUid, userId); + + return packageState == null + ? null + : packageState.getPageSizeCompatWarningMessage(mContext); + } + @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_USERS) @Override public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, @@ -6579,6 +6640,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + @NonNull + public List<String> getAllApexDirectories() { + PackageManagerServiceUtils.enforceSystemOrRoot( + "getAllApexDirectories can only be called by system or root"); + List<String> apexDirectories = new ArrayList<>(); + List<ApexManager.ActiveApexInfo> apexes = mApexManager.getActiveApexInfos(); + for (int i = 0; i < apexes.size(); i++) { + ApexManager.ActiveApexInfo apex = apexes.get(i); + apexDirectories.add(apex.apexDirectory.getAbsolutePath()); + } + return apexDirectories; + } + + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { try { diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 9428de700385..fb16b862b275 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -27,6 +27,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; @@ -221,6 +222,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal /** @see PackageState#getCategoryOverride() */ private int categoryOverride = ApplicationInfo.CATEGORY_UNDEFINED; + private int mPageSizeAppCompatFlags = ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED; + @NonNull private final PackageStateUnserialized pkgState = new PackageStateUnserialized(this); @@ -863,6 +866,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal } copyMimeGroups(other.mimeGroups); + mPageSizeAppCompatFlags = other.mPageSizeAppCompatFlags; + pkgState.updateFrom(other.pkgState); onChanged(); } @@ -1617,6 +1622,34 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return this; } + /** + * @see Set page size app compat mode. + */ + public PackageSetting setPageSizeAppCompatFlags(int mode) { + if (mode < 0 || mode >= ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MAX) { + throw new IllegalArgumentException("Invalid page size compat mode specified"); + } + + // OR assignment is used here to avoid overriding the mode set by the manifest. + this.mPageSizeAppCompatFlags |= mode; + + // Only one bit of the following can be set at same time. Both are needed to detect app + // compat 'disabled' state from settings vs bit was never set. + if (ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED == mode) { + this.mPageSizeAppCompatFlags &= + ~ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED; + } else if (ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED == mode) { + this.mPageSizeAppCompatFlags &= + ~ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED; + } + onChanged(); + return this; + } + + public int getPageSizeAppCompatFlags() { + return mPageSizeAppCompatFlags; + } + public PackageSetting setLegacyNativeLibraryPath( String legacyNativeLibraryPathString) { this.legacyNativeLibraryPath = legacyNativeLibraryPathString; @@ -1787,6 +1820,63 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return getBoolean(Booleans.SCANNED_AS_STOPPED_SYSTEM_APP); } + /** Returns true if ELF files will be loaded in Page size compatibility mode */ + @Override + public boolean isPageSizeAppCompatEnabled() { + // If manifest or settings has disabled the compat mode, don't run app in compat mode. + boolean manifestOverrideDisabled = (mPageSizeAppCompatFlags + & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED) != 0; + boolean settingsOverrideDisabled = (mPageSizeAppCompatFlags + & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED) != 0; + if (manifestOverrideDisabled || settingsOverrideDisabled) { + return false; + } + + int mask = + ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED + | ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED + | ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED; + return (mPageSizeAppCompatFlags & mask) != 0; + } + + /** + * Returns dialog string based on alignment of uncompressed shared libs inside the APK and ELF + * alignment. + */ + @Override + public String getPageSizeCompatWarningMessage(Context context) { + boolean manifestOverrideEnabled = (mPageSizeAppCompatFlags + & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0; + boolean settingsOverrideEnabled = (mPageSizeAppCompatFlags + & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0; + if (manifestOverrideEnabled || settingsOverrideEnabled) { + return null; + } + + boolean uncompressedLibsNotAligned = (mPageSizeAppCompatFlags + & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED) != 0; + boolean elfNotAligned = (mPageSizeAppCompatFlags + & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED) != 0; + + if (uncompressedLibsNotAligned && elfNotAligned) { + return context.getText( + com.android.internal.R.string.page_size_compat_apk_and_elf_warning) + .toString(); + } + + if (uncompressedLibsNotAligned) { + return context.getText(com.android.internal.R.string.page_size_compat_apk_warning) + .toString(); + } + + if (elfNotAligned) { + return context.getText(com.android.internal.R.string.page_size_compat_elf_warning) + .toString(); + } + + return null; + } + // Code below generated by codegen v1.0.23. @@ -1952,7 +2042,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal @Deprecated private void __metadata() {} - //@formatter:on // End of generated code diff --git a/services/core/java/com/android/server/pm/SaferIntentUtils.java b/services/core/java/com/android/server/pm/SaferIntentUtils.java index bc36fabc7f67..854e142c7c2f 100644 --- a/services/core/java/com/android/server/pm/SaferIntentUtils.java +++ b/services/core/java/com/android/server/pm/SaferIntentUtils.java @@ -26,6 +26,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.compat.annotation.EnabledAfter; import android.compat.annotation.Overridable; import android.content.Intent; @@ -88,6 +89,22 @@ public class SaferIntentUtils { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) private static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273; + /** + * Intents sent from apps enabling this feature will stop resolving to components with + * non matching intent filters, even when explicitly setting a component name, unless the + * target components are in the same app as the calling app. + * <p> + * When an app registers an exported component in its manifest and adds <intent-filter>s, + * the component can be started by any intent - even those that do not match the intent filter. + * This has proven to be something that many developers find counterintuitive. + * Without checking the intent when the component is started, in some circumstances this can + * allow 3P apps to trigger internal-only functionality. + */ + @ChangeId + @Overridable + @Disabled + private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188; + @Nullable private static ParsedMainComponent infoToComponent( ComponentInfo info, ComponentResolverApi resolver, boolean isReceiver) { @@ -249,6 +266,20 @@ public class SaferIntentUtils { */ public static void enforceIntentFilterMatching( IntentArgs args, List<ResolveInfo> resolveInfos) { + // Switch to the new intent matching logic if the feature flag is enabled. + // Otherwise, use the existing AppCompat based implementation. + if (Flags.enableIntentMatchingFlags()) { + enforceIntentFilterMatchingWithIntentMatchingFlags(args, resolveInfos); + } else { + enforceIntentFilterMatchingWithAppCompat(args, resolveInfos); + } + } + + /** + * This version of the method is implemented in Android B and uses "IntentMatchingFlags" + */ + private static void enforceIntentFilterMatchingWithIntentMatchingFlags( + IntentArgs args, List<ResolveInfo> resolveInfos) { if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; // Do not enforce filter matching when the caller is system or root @@ -339,6 +370,97 @@ public class SaferIntentUtils { } /** + * This version of the method is implemented in Android V and uses "AppCompat" + */ + private static void enforceIntentFilterMatchingWithAppCompat( + IntentArgs args, List<ResolveInfo> resolveInfos) { + if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; + + // Do not enforce filter matching when the caller is system or root + if (ActivityManager.canAccessUnexportedComponents(args.callingUid)) return; + + final Computer computer = (Computer) args.snapshot; + final ComponentResolverApi resolver = computer.getComponentResolver(); + + final Printer logPrinter = DEBUG_INTENT_MATCHING + ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) + : null; + + final boolean enforceMatch = Flags.enforceIntentFilterMatch() + && args.isChangeEnabled(ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS); + final boolean blockNullAction = Flags.blockNullActionIntents() + && args.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS); + + for (int i = resolveInfos.size() - 1; i >= 0; --i) { + final ComponentInfo info = resolveInfos.get(i).getComponentInfo(); + + // Skip filter matching when the caller is targeting the same app + if (UserHandle.isSameApp(args.callingUid, info.applicationInfo.uid)) { + continue; + } + + final ParsedMainComponent comp = infoToComponent(info, resolver, args.isReceiver); + + if (comp == null || comp.getIntents().isEmpty()) { + continue; + } + + Boolean match = null; + + if (args.intent.getAction() == null) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, + enforceMatch && blockNullAction); + if (blockNullAction) { + // Skip intent filter matching if blocking null action + match = false; + } + } + + if (match == null) { + // Check if any intent filter matches + for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { + IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); + if (IntentResolver.intentMatchesFilter( + intentFilter, args.intent, args.resolvedType)) { + match = true; + break; + } + } + } + + // At this point, the value `match` has the following states: + // null : Intent does not match any intent filter + // false: Null action intent detected AND blockNullAction == true + // true : The intent matches at least one intent filter + + if (match == null) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH, + enforceMatch); + match = false; + } + + if (!match) { + // All non-matching intents has to be marked accordingly + if (Flags.enforceIntentFilterMatch()) { + args.intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); + } + if (enforceMatch) { + Slog.w(TAG, "Intent does not match component's intent filter: " + args.intent); + Slog.w(TAG, "Access blocked: " + comp.getComponentName()); + if (DEBUG_INTENT_MATCHING) { + Slog.v(TAG, "Component intent filters:"); + comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, " ")); + Slog.v(TAG, "-----------------------------"); + } + resolveInfos.remove(i); + } + } + } + } + + /** * Filter non-exported components from the componentList if intent is implicit. * <p> * Implicit intents cannot be used to start Services since API 21+. diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 1f672a093b38..485a28070bc5 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3313,6 +3313,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (pkg.getBaseRevisionCode() != 0) { serializer.attributeInt(null, "baseRevisionCode", pkg.getBaseRevisionCode()); } + if (pkg.getPageSizeAppCompatFlags() + != ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED) { + serializer.attributeInt(null, "pageSizeCompat", pkg.getPageSizeAppCompatFlags()); + } + serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress()); serializer.attributeLongHex(null, "loadingCompletedTime", pkg.getLoadingCompletedTime()); @@ -4129,6 +4134,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile boolean isScannedAsStoppedSystemApp = false; boolean isSdkLibrary = false; int baseRevisionCode = 0; + int PageSizeCompat = 0; try { name = parser.getAttributeValue(null, ATTR_NAME); realName = parser.getAttributeValue(null, "realName"); @@ -4175,6 +4181,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile appMetadataSource = parser.getAttributeInt(null, "appMetadataSource", PackageManager.APP_METADATA_SOURCE_UNKNOWN); baseRevisionCode = parser.getAttributeInt(null, "baseRevisionCode", 0); + PageSizeCompat = parser.getAttributeInt(null, "pageSizeCompat", + ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED); isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null, "scannedAsStoppedSystemApp", false); @@ -4330,7 +4338,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile .setTargetSdkVersion(targetSdkVersion) .setBaseRevisionCode(baseRevisionCode) .setRestrictUpdateHash(restrictUpdateHash) - .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp); + .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp) + .setPageSizeAppCompatFlags(PageSizeCompat); // Handle legacy string here for single-user mode final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { @@ -5211,6 +5220,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile pw.print(" (override=true)"); } pw.println(); + pw.print(prefix); + pw.print(" pageSizeCompat="); + pw.print(ps.getPageSizeAppCompatFlags()); + pw.println(); if (!ps.getPkg().getQueriesPackages().isEmpty()) { pw.append(prefix).append(" queriesPackages=") .println(ps.getPkg().getQueriesPackages()); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b2b8aaf7dd2a..066fce068d61 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -6122,8 +6122,11 @@ public class UserManagerService extends IUserManager.Stub { // If the user switch hasn't been explicitly toggled on or off by the user, turn it on. if (android.provider.Settings.Global.getString(mContext.getContentResolver(), android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) { - android.provider.Settings.Global.putInt(mContext.getContentResolver(), - android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1); + if (Resources.getSystem().getBoolean( + com.android.internal.R.bool.config_enableUserSwitcherUponUserCreation)) { + android.provider.Settings.Global.putInt(mContext.getContentResolver(), + android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1); + } } } } diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java index bbc17c83cfac..33fc066a62ee 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageState.java +++ b/services/core/java/com/android/server/pm/pkg/PackageState.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.Size; import android.annotation.SystemApi; import android.annotation.UserIdInt; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -198,6 +199,21 @@ public interface PackageState { int getCategoryOverride(); /** + * Returns true if ELF files will be loaded in Page size compatibility mode + * + * @hide + */ + boolean isPageSizeAppCompatEnabled(); + + /** + * Returns dialog string based on alignment of uncompressed shared libs inside the APK and ELF + * alignment. + * + * @hide + */ + String getPageSizeCompatWarningMessage(Context context); + + /** * The install time CPU override, if any. This value is written at install time * and doesn't change during the life of an install. If non-null, * {@link #getPrimaryCpuAbiLegacy()} will also contain the same value. diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java index 253eb4006122..a46c4a695d60 100644 --- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java +++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java @@ -257,6 +257,16 @@ public class PackageStateMutator { @NonNull @Override + public PackageStateWrite setPageSizeAppCompatFlags( + @ApplicationInfo.PageSizeAppCompatFlags int mode) { + if (mState != null) { + mState.setPageSizeAppCompatFlags(mode); + } + return this; + } + + @NonNull + @Override public PackageStateWrite setUpdateAvailable(boolean updateAvailable) { if (mState != null) { mState.setUpdateAvailable(updateAvailable); diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java index 55d96f3aee08..f8f8695b2832 100644 --- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java +++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java @@ -46,6 +46,10 @@ public interface PackageStateWrite { @NonNull PackageStateWrite setCategoryOverride(@ApplicationInfo.Category int category); + /** set 16Kb App compat mode. @see ApplicationInfo.PageSizeAppCompatFlags */ + @NonNull + PackageStateWrite setPageSizeAppCompatFlags(@ApplicationInfo.PageSizeAppCompatFlags int mode); + @NonNull PackageStateWrite setUpdateAvailable(boolean updateAvailable); diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index 71f67d82dbec..aba15c83f907 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -34,7 +34,9 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.hardware.power.ChannelConfig; import android.hardware.power.CpuHeadroomParams; +import android.hardware.power.CpuHeadroomResult; import android.hardware.power.GpuHeadroomParams; +import android.hardware.power.GpuHeadroomResult; import android.hardware.power.IPower; import android.hardware.power.SessionConfig; import android.hardware.power.SessionTag; @@ -79,6 +81,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -96,10 +99,10 @@ public final class HintManagerService extends SystemService { private static final int EVENT_CLEAN_UP_UID = 3; @VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000; + // The minimum interval between the headroom calls as rate limiting. private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000; private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000; private static final int HEADROOM_INTERVAL_UNSUPPORTED = -1; - @VisibleForTesting static final int DEFAULT_HEADROOM_PID = -1; @VisibleForTesting final long mHintSessionPreferredRate; @@ -184,73 +187,77 @@ public final class HintManagerService extends SystemService { private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint"; private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager"; private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms"; + private static final String PROPERTY_CHECK_HEADROOM_TID = "persist.hms.check_headroom_tid"; private Boolean mFMQUsesIntegratedEventFlag = false; private final Object mCpuHeadroomLock = new Object(); - private static class CpuHeadroomCacheItem { - long mExpiredTimeMillis; - CpuHeadroomParamsInternal mParams; - float[] mHeadroom; - long mPid; - CpuHeadroomCacheItem(long expiredTimeMillis, CpuHeadroomParamsInternal params, - float[] headroom, long pid) { - mExpiredTimeMillis = expiredTimeMillis; - mParams = params; - mPid = pid; - mHeadroom = headroom; - } + // this cache tracks the expiration time of the items and performs cleanup on lookup + private static class HeadroomCache<K, V> { + final List<HeadroomCacheItem> mItemList; + final Map<K, HeadroomCacheItem> mKeyItemMap; + final long mItemExpDurationMillis; + + class HeadroomCacheItem { + long mExpTime; + K mKey; + V mValue; - private boolean match(CpuHeadroomParamsInternal params, long pid) { - if (mParams == null && params == null) return true; - if (mParams != null) { - return mParams.equals(params) && pid == mPid; + HeadroomCacheItem(K k, V v) { + mExpTime = System.currentTimeMillis() + mItemExpDurationMillis; + mKey = k; + mValue = v; } - return false; - } - private boolean isExpired() { - return System.currentTimeMillis() > mExpiredTimeMillis; + boolean isExpired() { + return mExpTime <= System.currentTimeMillis(); + } } - } - - @GuardedBy("mCpuHeadroomLock") - private final List<CpuHeadroomCacheItem> mCpuHeadroomCache; - private final long mCpuHeadroomIntervalMillis; - - private final Object mGpuHeadroomLock = new Object(); - private static class GpuHeadroomCacheItem { - long mExpiredTimeMillis; - GpuHeadroomParamsInternal mParams; - float mHeadroom; - - GpuHeadroomCacheItem(long expiredTimeMillis, GpuHeadroomParamsInternal params, - float headroom) { - mExpiredTimeMillis = expiredTimeMillis; - mParams = params; - mHeadroom = headroom; + void add(K key, V value) { + if (mKeyItemMap.containsKey(key)) { + final HeadroomCacheItem item = mKeyItemMap.get(key); + mItemList.remove(item); + } + final HeadroomCacheItem item = new HeadroomCacheItem(key, value); + mItemList.add(item); + mKeyItemMap.put(key, item); } - private boolean match(GpuHeadroomParamsInternal params) { - if (mParams == null && params == null) return true; - if (mParams != null) { - return mParams.equals(params); + V get(K key) { + while (!mItemList.isEmpty() && mItemList.getFirst().isExpired()) { + mKeyItemMap.remove(mItemList.removeFirst().mKey); } - return false; + final HeadroomCacheItem item = mKeyItemMap.get(key); + if (item == null) { + return null; + } + return item.mValue; } - private boolean isExpired() { - return System.currentTimeMillis() > mExpiredTimeMillis; + HeadroomCache(int size, long expDurationMillis) { + mItemList = new LinkedList<>(); + mKeyItemMap = new ArrayMap<>(size); + mItemExpDurationMillis = expDurationMillis; } } + @GuardedBy("mCpuHeadroomLock") + private final HeadroomCache<CpuHeadroomParams, CpuHeadroomResult> mCpuHeadroomCache; + private final long mCpuHeadroomIntervalMillis; + + private final Object mGpuHeadroomLock = new Object(); + @GuardedBy("mGpuHeadroomLock") - private final List<GpuHeadroomCacheItem> mGpuHeadroomCache; + private final HeadroomCache<GpuHeadroomParams, GpuHeadroomResult> mGpuHeadroomCache; private final long mGpuHeadroomIntervalMillis; + // these are set to default values in CpuHeadroomParamsInternal and GpuHeadroomParamsInternal + private final int mDefaultCpuHeadroomCalculationWindowMillis; + private final int mDefaultGpuHeadroomCalculationWindowMillis; + @VisibleForTesting final IHintManager.Stub mService = new BinderService(); @@ -303,26 +310,31 @@ public final class HintManagerService extends SystemService { } } mCpuHeadroomIntervalMillis = cpuHeadroomIntervalMillis; + mDefaultCpuHeadroomCalculationWindowMillis = + new CpuHeadroomParamsInternal().calculationWindowMillis; + mDefaultGpuHeadroomCalculationWindowMillis = + new GpuHeadroomParamsInternal().calculationWindowMillis; mGpuHeadroomIntervalMillis = gpuHeadroomIntervalMillis; if (mCpuHeadroomIntervalMillis > 0) { - mCpuHeadroomCache = new ArrayList<>(4); + mCpuHeadroomCache = new HeadroomCache<>(2, mCpuHeadroomIntervalMillis); } else { mCpuHeadroomCache = null; } if (mGpuHeadroomIntervalMillis > 0) { - mGpuHeadroomCache = new ArrayList<>(2); + mGpuHeadroomCache = new HeadroomCache<>(2, mGpuHeadroomIntervalMillis); } else { mGpuHeadroomCache = null; } } private long checkCpuHeadroomSupport() { + final CpuHeadroomParams params = new CpuHeadroomParams(); + params.tids = new int[]{Process.myPid()}; try { synchronized (mCpuHeadroomLock) { - final CpuHeadroomParams defaultParams = new CpuHeadroomParams(); - defaultParams.pid = Process.myPid(); - float[] ret = mPowerHal.getCpuHeadroom(defaultParams); - if (ret != null && ret.length > 0) { + final CpuHeadroomResult ret = mPowerHal.getCpuHeadroom(params); + if (ret != null && ret.getTag() == CpuHeadroomResult.globalHeadroom + && !Float.isNaN(ret.getGlobalHeadroom())) { return Math.max( DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS, mPowerHal.getCpuHeadroomMinIntervalMillis()); @@ -330,27 +342,29 @@ public final class HintManagerService extends SystemService { } } catch (UnsupportedOperationException e) { - Slog.w(TAG, "getCpuHeadroom HAL API is not supported", e); + Slog.w(TAG, "getCpuHeadroom HAL API is not supported, params: " + params, e); } catch (RemoteException e) { - Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API", e); + Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API, params: " + params, e); } return HEADROOM_INTERVAL_UNSUPPORTED; } private long checkGpuHeadroomSupport() { + final GpuHeadroomParams params = new GpuHeadroomParams(); try { synchronized (mGpuHeadroomLock) { - float ret = mPowerHal.getGpuHeadroom(new GpuHeadroomParams()); - if (!Float.isNaN(ret)) { + final GpuHeadroomResult ret = mPowerHal.getGpuHeadroom(params); + if (ret != null && ret.getTag() == GpuHeadroomResult.globalHeadroom && !Float.isNaN( + ret.getGlobalHeadroom())) { return Math.max( DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS, mPowerHal.getGpuHeadroomMinIntervalMillis()); } } } catch (UnsupportedOperationException e) { - Slog.w(TAG, "getGpuHeadroom HAL API is not supported", e); + Slog.w(TAG, "getGpuHeadroom HAL API is not supported, params: " + params, e); } catch (RemoteException e) { - Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API", e); + Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API, params: " + params, e); } return HEADROOM_INTERVAL_UNSUPPORTED; } @@ -1445,109 +1459,98 @@ public final class HintManagerService extends SystemService { } @Override - public float[] getCpuHeadroom(@Nullable CpuHeadroomParamsInternal params) { + public CpuHeadroomResult getCpuHeadroom(@NonNull CpuHeadroomParamsInternal params) { if (mCpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } - CpuHeadroomParams halParams = new CpuHeadroomParams(); - halParams.pid = Binder.getCallingPid(); - if (params != null) { - halParams.calculationType = params.calculationType; - halParams.selectionType = params.selectionType; - if (params.usesDeviceHeadroom) { - halParams.pid = DEFAULT_HEADROOM_PID; - } - } - synchronized (mCpuHeadroomLock) { - while (!mCpuHeadroomCache.isEmpty()) { - if (mCpuHeadroomCache.getFirst().isExpired()) { - mCpuHeadroomCache.removeFirst(); - } else { - break; + final CpuHeadroomParams halParams = new CpuHeadroomParams(); + halParams.tids = new int[]{Binder.getCallingPid()}; + halParams.calculationType = params.calculationType; + halParams.calculationWindowMillis = params.calculationWindowMillis; + halParams.selectionType = params.selectionType; + if (params.usesDeviceHeadroom) { + halParams.tids = new int[]{}; + } else if (params.tids != null && params.tids.length > 0) { + if (params.tids.length > 5) { + throw new IllegalArgumentException( + "More than 5 TIDs is requested: " + params.tids.length); + } + if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) { + final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid()); + for (int tid : params.tids) { + if (Process.getThreadGroupLeader(tid) != tgid) { + throw new SecurityException("TID " + tid + + " doesn't belong to the calling process with pid " + + tgid); + } } } - for (int i = 0; i < mCpuHeadroomCache.size(); ++i) { - final CpuHeadroomCacheItem item = mCpuHeadroomCache.get(i); - if (item.match(params, halParams.pid)) { - item.mExpiredTimeMillis = - System.currentTimeMillis() + mCpuHeadroomIntervalMillis; - mCpuHeadroomCache.remove(i); - mCpuHeadroomCache.add(item); - return item.mHeadroom; - } + halParams.tids = params.tids; + } + if (halParams.calculationWindowMillis + == mDefaultCpuHeadroomCalculationWindowMillis) { + synchronized (mCpuHeadroomLock) { + final CpuHeadroomResult res = mCpuHeadroomCache.get(halParams); + if (res != null) return res; } } // return from HAL directly try { - float[] headroom = mPowerHal.getCpuHeadroom(halParams); - if (headroom == null || headroom.length == 0) { - Slog.wtf(TAG, - "CPU headroom from Power HAL is invalid: " + Arrays.toString(headroom)); - return new float[]{Float.NaN}; - } - synchronized (mCpuHeadroomLock) { - mCpuHeadroomCache.add(new CpuHeadroomCacheItem( - System.currentTimeMillis() + mCpuHeadroomIntervalMillis, - params, headroom, halParams.pid - )); + final CpuHeadroomResult result = mPowerHal.getCpuHeadroom(halParams); + if (result == null) { + Slog.wtf(TAG, "CPU headroom from Power HAL is invalid"); + return null; + } + if (halParams.calculationWindowMillis + == mDefaultCpuHeadroomCalculationWindowMillis) { + synchronized (mCpuHeadroomLock) { + mCpuHeadroomCache.add(halParams, result); + } } - return headroom; - + return result; } catch (RemoteException e) { - return new float[]{Float.NaN}; + Slog.e(TAG, "Failed to get CPU headroom from Power HAL", e); + return null; } } @Override - public float getGpuHeadroom(@Nullable GpuHeadroomParamsInternal params) { + public GpuHeadroomResult getGpuHeadroom(@NonNull GpuHeadroomParamsInternal params) { if (mGpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } - GpuHeadroomParams halParams = new GpuHeadroomParams(); - if (params != null) { - halParams.calculationType = params.calculationType; - } - synchronized (mGpuHeadroomLock) { - while (!mGpuHeadroomCache.isEmpty()) { - if (mGpuHeadroomCache.getFirst().isExpired()) { - mGpuHeadroomCache.removeFirst(); - } else { - break; - } - } - for (int i = 0; i < mGpuHeadroomCache.size(); ++i) { - final GpuHeadroomCacheItem item = mGpuHeadroomCache.get(i); - if (item.match(params)) { - item.mExpiredTimeMillis = - System.currentTimeMillis() + mGpuHeadroomIntervalMillis; - mGpuHeadroomCache.remove(i); - mGpuHeadroomCache.add(item); - return item.mHeadroom; - } + final GpuHeadroomParams halParams = new GpuHeadroomParams(); + halParams.calculationType = params.calculationType; + halParams.calculationWindowMillis = params.calculationWindowMillis; + if (halParams.calculationWindowMillis + == mDefaultGpuHeadroomCalculationWindowMillis) { + synchronized (mGpuHeadroomLock) { + final GpuHeadroomResult res = mGpuHeadroomCache.get(halParams); + if (res != null) return res; } } // return from HAL directly try { - float headroom = mPowerHal.getGpuHeadroom(halParams); - if (Float.isNaN(headroom)) { - Slog.wtf(TAG, - "GPU headroom from Power HAL is NaN"); - return Float.NaN; - } - synchronized (mGpuHeadroomLock) { - mGpuHeadroomCache.add(new GpuHeadroomCacheItem( - System.currentTimeMillis() + mGpuHeadroomIntervalMillis, - params, headroom - )); + final GpuHeadroomResult headroom = mPowerHal.getGpuHeadroom(halParams); + if (headroom == null) { + Slog.wtf(TAG, "GPU headroom from Power HAL is invalid"); + return null; + } + if (halParams.calculationWindowMillis + == mDefaultGpuHeadroomCalculationWindowMillis) { + synchronized (mGpuHeadroomLock) { + mGpuHeadroomCache.add(halParams, headroom); + } } return headroom; } catch (RemoteException e) { - return Float.NaN; + Slog.e(TAG, "Failed to get GPU headroom from Power HAL", e); + return null; } } @Override - public long getCpuHeadroomMinIntervalMillis() throws RemoteException { + public long getCpuHeadroomMinIntervalMillis() { if (mCpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } @@ -1555,7 +1558,7 @@ public final class HintManagerService extends SystemService { } @Override - public long getGpuHeadroomMinIntervalMillis() throws RemoteException { + public long getGpuHeadroomMinIntervalMillis() { if (mGpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } @@ -1591,17 +1594,23 @@ public final class HintManagerService extends SystemService { CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal(); params.selectionType = CpuHeadroomParams.SelectionType.ALL; params.usesDeviceHeadroom = true; - pw.println("CPU headroom: " + Arrays.toString(getCpuHeadroom(params))); + CpuHeadroomResult ret = getCpuHeadroom(params); + pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); params = new CpuHeadroomParamsInternal(); params.selectionType = CpuHeadroomParams.SelectionType.PER_CORE; params.usesDeviceHeadroom = true; - pw.println("CPU headroom per core: " + Arrays.toString(getCpuHeadroom(params))); + ret = getCpuHeadroom(params); + pw.println("CPU headroom per core: " + (ret == null ? "N/A" + : Arrays.toString(ret.getPerCoreHeadroom()))); } catch (Exception e) { + Slog.d(TAG, "Failed to dump CPU headroom", e); pw.println("CPU headroom: N/A"); } try { - pw.println("GPU headroom: " + getGpuHeadroom(null)); + GpuHeadroomResult ret = getGpuHeadroom(new GpuHeadroomParamsInternal()); + pw.println("GPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); } catch (Exception e) { + Slog.d(TAG, "Failed to dump GPU headroom", e); pw.println("GPU headroom: N/A"); } } diff --git a/services/core/java/com/android/server/power/hint/adpf_flags.aconfig b/services/core/java/com/android/server/power/hint/adpf_flags.aconfig index 147d76bda477..97d34836c70c 100644 --- a/services/core/java/com/android/server/power/hint/adpf_flags.aconfig +++ b/services/core/java/com/android/server/power/hint/adpf_flags.aconfig @@ -5,3 +5,11 @@ package: "android.adpf" container: "system" +flag { + name: "adpf_viewrootimpl_action_down_boost" + is_exported: true + namespace: "game" + description: "Guards boosting on touch in ViewRootImpl." + is_fixed_read_only: true + bug: "360345939" +} diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig index e56b68c93480..c231f5a6caec 100644 --- a/services/core/java/com/android/server/power/hint/flags.aconfig +++ b/services/core/java/com/android/server/power/hint/flags.aconfig @@ -21,3 +21,10 @@ flag { description: "Set reset_on_fork flag." bug: "370988407" } + +flag { + name: "cpu_headroom_affinity_check" + namespace: "game" + description: "Check affinity on CPU headroom." + bug: "346604998" +} diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index 606bd1dd0f3f..63e8d9973237 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -26,6 +26,7 @@ import android.os.BatteryUsageStatsQuery; import android.os.Handler; import android.os.Process; import android.util.Log; +import android.util.LogWriter; import android.util.Slog; import android.util.SparseArray; @@ -34,6 +35,7 @@ import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.MonotonicClock; import com.android.internal.os.PowerProfile; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,6 +46,8 @@ import java.util.List; */ public class BatteryUsageStatsProvider { private static final String TAG = "BatteryUsageStatsProv"; + private static final boolean DEBUG = false; + private final Context mContext; private final PowerAttributor mPowerAttributor; private final PowerStatsStore mPowerStatsStore; @@ -262,17 +266,25 @@ public class BatteryUsageStatsProvider { private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long currentTimeMs) { + BatteryUsageStats batteryUsageStats; if ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_ACCUMULATED) != 0) { - return getAccumulatedBatteryUsageStats(stats, query, currentTimeMs); + batteryUsageStats = getAccumulatedBatteryUsageStats(stats, query, currentTimeMs); } else if (query.getAggregatedToTimestamp() == 0) { BatteryUsageStats.Builder builder = computeBatteryUsageStats(stats, query, query.getMonotonicStartTime(), query.getMonotonicEndTime(), currentTimeMs); - return builder.build(); + batteryUsageStats = builder.build(); } else { - return getAggregatedBatteryUsageStats(stats, query); + batteryUsageStats = getAggregatedBatteryUsageStats(stats, query); + } + if (DEBUG) { + Slog.d(TAG, "query = " + query); + PrintWriter pw = new PrintWriter(new LogWriter(Log.DEBUG, TAG)); + batteryUsageStats.dump(pw, ""); + pw.flush(); } + return batteryUsageStats; } private BatteryUsageStats getAccumulatedBatteryUsageStats(BatteryStatsImpl stats, @@ -319,7 +331,7 @@ public class BatteryUsageStatsProvider { if (accumulatedStats.builder == null) { accumulatedStats.builder = new BatteryUsageStats.Builder( - stats.getCustomEnergyConsumerNames(), false, true, true, true, 0); + stats.getCustomEnergyConsumerNames(), true, true, true, 0); accumulatedStats.startWallClockTime = stats.getStartClockTime(); accumulatedStats.builder.setStatsStartTimestamp(accumulatedStats.startWallClockTime); } @@ -338,8 +350,6 @@ public class BatteryUsageStatsProvider { private BatteryUsageStats.Builder computeBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long monotonicStartTime, long monotonicEndTime, long currentTimeMs) { - final boolean includePowerModels = (query.getFlags() - & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final boolean includeProcessStateData = ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0) && stats.isProcessStateDataAvailable(); @@ -353,8 +363,7 @@ public class BatteryUsageStatsProvider { } final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder( - customEnergyConsumerNames, includePowerModels, - includeProcessStateData, query.isScreenStateDataNeeded(), + customEnergyConsumerNames, includeProcessStateData, query.isScreenStateDataNeeded(), query.isPowerStateDataNeeded(), minConsumedPowerThreshold); synchronized (stats) { @@ -444,8 +453,6 @@ public class BatteryUsageStatsProvider { private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query) { - final boolean includePowerModels = (query.getFlags() - & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final boolean includeProcessStateData = ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0) && stats.isProcessStateDataAvailable(); @@ -453,7 +460,7 @@ public class BatteryUsageStatsProvider { final String[] customEnergyConsumerNames = stats.getCustomEnergyConsumerNames(); final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - customEnergyConsumerNames, includePowerModels, includeProcessStateData, + customEnergyConsumerNames, includeProcessStateData, query.isScreenStateDataNeeded(), query.isPowerStateDataNeeded(), minConsumedPowerThreshold); if (mPowerStatsStore == null) { diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java index b2442c81b2e5..ef0e63bece90 100644 --- a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java +++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java @@ -224,13 +224,12 @@ class PowerStatsExporter { BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, deviceScope, powerComponentId, screenState, powerState); if (key != null) { - deviceScope.addConsumedPower(key, totalPower[0], - BatteryConsumer.POWER_MODEL_UNDEFINED); + deviceScope.addConsumedPower(key, totalPower[0]); deviceScope.addUsageDurationMillis(key, durationMs[0]); } key = deviceScope.getKey(powerComponentId, BatteryConsumer.PROCESS_STATE_UNSPECIFIED); if (key != null) { - deviceScope.addConsumedPower(key, totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED); + deviceScope.addConsumedPower(key, totalPower[0]); deviceScope.addUsageDurationMillis(key, durationMs[0]); } } @@ -373,7 +372,7 @@ class PowerStatsExporter { BatteryConsumer.Key key = builder.getKey(powerComponentId, procState, resultScreenState, resultPowerState); if (key != null) { - builder.addConsumedPower(key, power, BatteryConsumer.POWER_MODEL_UNDEFINED); + builder.addConsumedPower(key, power); builder.addUsageDurationMillis(key, duration); } } @@ -384,8 +383,7 @@ class PowerStatsExporter { BatteryConsumer.PROCESS_STATE_UNSPECIFIED); if (key != null) { builder.addConsumedPower(key, - powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED], - BatteryConsumer.POWER_MODEL_UNDEFINED); + powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED]); builder.addUsageDurationMillis(key, durationByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED]); } @@ -399,11 +397,9 @@ class PowerStatsExporter { BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, allAppsScope, powerComponentId, screenState, powerState); if (key != null) { - allAppsScope.addConsumedPower(key, powerAllApps, - BatteryConsumer.POWER_MODEL_UNDEFINED); + allAppsScope.addConsumedPower(key, powerAllApps); } - allAppsScope.addConsumedPower(powerComponentId, powerAllApps, - BatteryConsumer.POWER_MODEL_UNDEFINED); + allAppsScope.addConsumedPower(powerComponentId, powerAllApps); } @Nullable diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java index e8723b91a541..2cc08c327b71 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java @@ -46,7 +46,9 @@ import com.android.server.SystemService; import com.android.server.pm.UserManagerInternal; import com.android.server.security.advancedprotection.features.AdvancedProtectionHook; import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider; +import com.android.server.security.advancedprotection.features.DisallowCellular2GAdvancedProtectionHook; import com.android.server.security.advancedprotection.features.DisallowInstallUnknownSourcesAdvancedProtectionHook; +import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook; import java.io.FileDescriptor; import java.util.ArrayList; @@ -80,6 +82,12 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub if (android.security.Flags.aapmFeatureDisableInstallUnknownSources()) { mHooks.add(new DisallowInstallUnknownSourcesAdvancedProtectionHook(mContext, enabled)); } + if (android.security.Flags.aapmFeatureMemoryTaggingExtension()) { + mHooks.add(new MemoryTaggingExtensionHook(mContext, enabled)); + } + if (android.security.Flags.aapmFeatureDisableCellular2g()) { + mHooks.add(new DisallowCellular2GAdvancedProtectionHook(mContext, enabled)); + } } // Only for tests diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java new file mode 100644 index 000000000000..b9c8d3dc5319 --- /dev/null +++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.advancedprotection.features; + +import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY; +import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G; + +import android.annotation.NonNull; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.os.UserManager; +import android.security.advancedprotection.AdvancedProtectionFeature; +import android.telephony.TelephonyManager; +import android.util.Slog; + +/** @hide */ +public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProtectionHook { + private static final String TAG = "AdvancedProtectionDisallowCellular2G"; + + private final AdvancedProtectionFeature mFeature = + new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_CELLULAR_2G); + private final DevicePolicyManager mDevicePolicyManager; + private final TelephonyManager mTelephonyManager; + + public DisallowCellular2GAdvancedProtectionHook(@NonNull Context context, boolean enabled) { + super(context, enabled); + mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); + mTelephonyManager = context.getSystemService(TelephonyManager.class); + + setPolicy(enabled); + } + + @NonNull + @Override + public AdvancedProtectionFeature getFeature() { + return mFeature; + } + + @Override + public boolean isAvailable() { + return mTelephonyManager.isDataCapable(); + } + + private void setPolicy(boolean enabled) { + Slog.i(TAG, "setPolicy called with " + enabled); + + if (enabled) { + Slog.d(TAG, "Setting DISALLOW_CELLULAR_2G_GLOBALLY restriction"); + mDevicePolicyManager.addUserRestrictionGlobally( + ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G); + } else { + Slog.d(TAG, "Clearing DISALLOW_CELLULAR_2G_GLOBALLY restriction"); + mDevicePolicyManager.clearUserRestrictionGlobally( + ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G); + } + } + + @Override + public void onAdvancedProtectionChanged(boolean enabled) { + setPolicy(enabled); + + // Leave 2G disabled even if APM is disabled. + if (!enabled) { + long oldAllowedTypes = + mTelephonyManager.getAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G); + long newAllowedTypes = oldAllowedTypes & ~TelephonyManager.NETWORK_CLASS_BITMASK_2G; + mTelephonyManager.setAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedTypes); + } + } +} diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java index 21752e524619..a2933d96e4a8 100644 --- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java +++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java @@ -19,13 +19,24 @@ package com.android.server.security.advancedprotection.features; import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY; import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES; +import android.Manifest; import android.annotation.NonNull; +import android.app.ActivityManagerInternal; +import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.Process; +import android.os.RemoteException; import android.os.UserManager; import android.security.advancedprotection.AdvancedProtectionFeature; import android.util.Slog; +import com.android.server.LocalServices; + /** @hide */ public final class DisallowInstallUnknownSourcesAdvancedProtectionHook extends AdvancedProtectionHook { @@ -33,13 +44,25 @@ public final class DisallowInstallUnknownSourcesAdvancedProtectionHook private final AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature( FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES); + + private final ActivityManagerInternal mActivityManagerInternal; + private final AppOpsManager mAppOpsManager; private final DevicePolicyManager mDevicePolicyManager; + private final IPackageManager mIPackageManager; + private final PackageManager mPackageManager; + private final UserManager mUserManager; public DisallowInstallUnknownSourcesAdvancedProtectionHook(@NonNull Context context, boolean enabled) { super(context, enabled); + mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + mAppOpsManager = context.getSystemService(AppOpsManager.class); mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); - onAdvancedProtectionChanged(enabled); + mIPackageManager = AppGlobals.getPackageManager(); + mUserManager = context.getSystemService(UserManager.class); + mPackageManager = context.getPackageManager(); + + setRestriction(enabled); } @NonNull @@ -53,21 +76,48 @@ public final class DisallowInstallUnknownSourcesAdvancedProtectionHook return true; } - @Override - public void onAdvancedProtectionChanged(boolean enabled) { + private void setRestriction(boolean enabled) { if (enabled) { Slog.d(TAG, "Setting DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction"); mDevicePolicyManager.addUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY); - return; + } else { + Slog.d(TAG, "Clearing DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction"); + mDevicePolicyManager.clearUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY); } - Slog.d(TAG, "Clearing DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction"); - mDevicePolicyManager.clearUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY, - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY); + } - // TODO(b/369361373): - // 1. After clearing the restriction, set AppOpsManager.OP_REQUEST_INSTALL_PACKAGES to - // disabled. - // 2. Update dialog strings. + @Override + public void onAdvancedProtectionChanged(boolean enabled) { + setRestriction(enabled); + if (enabled) return; + + // Leave OP_REQUEST_INSTALL_PACKAGES disabled when APM is disabled. + Slog.d(TAG, "Setting all OP_REQUEST_INSTALL_PACKAGES to MODE_ERRORED"); + for (UserInfo userInfo : mUserManager.getAliveUsers()) { + try { + final String[] packagesWithRequestInstallPermission = mIPackageManager + .getAppOpPermissionPackages( + Manifest.permission.REQUEST_INSTALL_PACKAGES, userInfo.id); + for (String packageName : packagesWithRequestInstallPermission) { + try { + int uid = mPackageManager.getPackageUidAsUser(packageName, userInfo.id); + boolean isCallerInstrumented = mActivityManagerInternal + .getInstrumentationSourceUid(uid) != Process.INVALID_UID; + if (!isCallerInstrumented) { + mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid, + packageName, AppOpsManager.MODE_ERRORED); + } + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Couldn't retrieve uid for a package: " + e); + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Couldn't retrieve packages with REQUEST_INSTALL_PACKAGES." + + " getAppOpPermissionPackages() threw the following exception: " + e); + } + } + // TODO(b/369361373): Update dialog strings. } } diff --git a/services/core/java/com/android/server/security/advancedprotection/features/MemoryTaggingExtensionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/MemoryTaggingExtensionHook.java new file mode 100644 index 000000000000..d3d39371e883 --- /dev/null +++ b/services/core/java/com/android/server/security/advancedprotection/features/MemoryTaggingExtensionHook.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.advancedprotection.features; + +import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY; +import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE; + +import android.annotation.NonNull; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.os.SystemProperties; +import android.security.advancedprotection.AdvancedProtectionFeature; +import android.util.Slog; + +/** @hide */ +public final class MemoryTaggingExtensionHook + extends AdvancedProtectionHook { + private static final String TAG = "AdvancedProtectionMTE"; + private static final String MTE_DPM_SYSTEM_PROPERTY = + "ro.arm64.memtag.bootctl_device_policy_manager"; + private static final String MTE_SETTINGS_SYSTEM_PROPERTY = + "ro.arm64.memtag.bootctl_settings_toggle"; + + private final AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature( + FEATURE_ID_ENABLE_MTE); + private final DevicePolicyManager mDevicePolicyManager; + + public MemoryTaggingExtensionHook(@NonNull Context context, + boolean enabled) { + super(context, enabled); + mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); + onAdvancedProtectionChanged(enabled); + } + + @NonNull + @Override + public AdvancedProtectionFeature getFeature() { + return mFeature; + } + + @Override + public boolean isAvailable() { + return SystemProperties.getBoolean(MTE_DPM_SYSTEM_PROPERTY, + SystemProperties.getBoolean(MTE_SETTINGS_SYSTEM_PROPERTY, false)); + } + + @Override + public void onAdvancedProtectionChanged(boolean enabled) { + if (!isAvailable()) { + Slog.i(TAG, "MTE unavailable on device, skipping."); + return; + } + final int mtePolicy; + if (enabled) { + mtePolicy = DevicePolicyManager.MTE_ENABLED; + } else { + mtePolicy = DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY; + } + + Slog.d(TAG, "Setting MTE state to " + mtePolicy); + try { + mDevicePolicyManager.setMtePolicy(ADVANCED_PROTECTION_SYSTEM_ENTITY, mtePolicy); + } catch (UnsupportedOperationException e) { + Slog.i(TAG, "Setting MTE policy unsupported", e); + } + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java index 06e9dcdcbedd..0ea88e8523f0 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java @@ -36,6 +36,9 @@ public class DataAggregator { private static final int MSG_DISABLE = 2; private static final int STORED_EVENTS_SIZE_LIMIT = 1024; + private static final IntrusionDetectionAdminReceiver ADMIN_RECEIVER = + new IntrusionDetectionAdminReceiver(); + private final IntrusionDetectionService mIntrusionDetectionService; private final ArrayList<DataSource> mDataSources; @@ -60,10 +63,19 @@ public class DataAggregator { * Initialize DataSources * @return Whether the initialization succeeds. */ - // TODO: Add the corresponding data sources public boolean initialize() { SecurityLogSource securityLogSource = new SecurityLogSource(mContext, this); mDataSources.add(securityLogSource); + + NetworkLogSource networkLogSource = new NetworkLogSource(mContext, this); + ADMIN_RECEIVER.setNetworkLogEventCallback(networkLogSource); + mDataSources.add(networkLogSource); + + for (DataSource ds : mDataSources) { + if (!ds.initialize()) { + return false; + } + } return true; } diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java index 0bc448245b76..61fac46be82d 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java @@ -18,6 +18,11 @@ package com.android.server.security.intrusiondetection; public interface DataSource { /** + * Initialize the data source. + */ + boolean initialize(); + + /** * Enable the data collection. */ void enable(); diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java new file mode 100644 index 000000000000..dba7374fe02a --- /dev/null +++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.intrusiondetection; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Slog; + +public class IntrusionDetectionAdminReceiver extends DeviceAdminReceiver { + private static final String TAG = "IntrusionDetectionAdminReceiver"; + + private static NetworkLogSource sNetworkLogSource; + + @Override + public void onNetworkLogsAvailable( + Context context, Intent intent, long batchToken, int networkLogsCount) { + if (sNetworkLogSource != null) { + sNetworkLogSource.onNetworkLogsAvailable(batchToken); + } else { + Slog.w(TAG, "Network log receiver is not initialized"); + } + } + + public void setNetworkLogEventCallback(NetworkLogSource networkLogSource) { + sNetworkLogSource = networkLogSource; + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java new file mode 100644 index 000000000000..1c93d3f9c6a1 --- /dev/null +++ b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.intrusiondetection; + +import android.app.admin.ConnectEvent; +import android.app.admin.DevicePolicyManager; +import android.app.admin.DnsEvent; +import android.app.admin.NetworkEvent; +import android.content.ComponentName; +import android.content.Context; +import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.util.Slog; + +import java.util.List; +import java.util.stream.Collectors; + +public class NetworkLogSource implements DataSource { + + private static final String TAG = "IntrusionDetectionEvent NetworkLogSource"; + + private DevicePolicyManager mDpm; + private ComponentName mAdmin; + private DataAggregator mDataAggregator; + + public NetworkLogSource(Context context, DataAggregator dataAggregator) { + mDataAggregator = dataAggregator; + mDpm = context.getSystemService(DevicePolicyManager.class); + mAdmin = new ComponentName(context, IntrusionDetectionAdminReceiver.class); + } + + @Override + public boolean initialize() { + try { + if (!mDpm.isAdminActive(mAdmin)) { + Slog.e(TAG, "Admin " + mAdmin.flattenToString() + "is not active admin"); + return false; + } + } catch (SecurityException e) { + Slog.e(TAG, "Security exception in initialize: ", e); + return false; + } + return true; + } + + @Override + public void enable() { + enableNetworkLog(); + } + + @Override + public void disable() { + disableNetworkLog(); + } + + private void enableNetworkLog() { + if (!isNetworkLogEnabled()) { + mDpm.setNetworkLoggingEnabled(mAdmin, true); + } + } + + private void disableNetworkLog() { + if (isNetworkLogEnabled()) { + mDpm.setNetworkLoggingEnabled(mAdmin, false); + } + } + + private boolean isNetworkLogEnabled() { + return mDpm.isNetworkLoggingEnabled(mAdmin); + } + + /** + * Retrieve network logs when onNetworkLogsAvailable callback is received. + * + * @param batchToken The token representing the current batch of network logs. + */ + public void onNetworkLogsAvailable(long batchToken) { + List<NetworkEvent> events; + try { + events = mDpm.retrieveNetworkLogs(mAdmin, batchToken); + } catch (SecurityException e) { + Slog.e( + TAG, + "Admin " + + mAdmin.flattenToString() + + "does not have permission to retrieve network logs", + e); + return; + } + if (events == null) { + if (!isNetworkLogEnabled()) { + Slog.w(TAG, "Network logging is disabled"); + } else { + Slog.e(TAG, "Invalid batch token: " + batchToken); + } + return; + } + + List<IntrusionDetectionEvent> intrusionDetectionEvents = + events.stream() + .filter(event -> event != null) + .map(event -> toIntrusionDetectionEvent(event)) + .collect(Collectors.toList()); + mDataAggregator.addBatchData(intrusionDetectionEvents); + } + + private IntrusionDetectionEvent toIntrusionDetectionEvent(NetworkEvent event) { + if (event instanceof DnsEvent) { + DnsEvent dnsEvent = (DnsEvent) event; + return new IntrusionDetectionEvent(dnsEvent); + } else if (event instanceof ConnectEvent) { + ConnectEvent connectEvent = (ConnectEvent) event; + return new IntrusionDetectionEvent(connectEvent); + } + throw new IllegalArgumentException( + "Invalid event type with ID: " + + event.getId() + + "from package: " + + event.getPackageName()); + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java index 226f9d879cab..c5f736e383b2 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.SecurityLog.SecurityEvent; import android.content.Context; import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.util.Slog; import java.util.List; import java.util.concurrent.Executor; @@ -33,7 +34,7 @@ public class SecurityLogSource implements DataSource { private static final String TAG = "IntrusionDetection SecurityLogSource"; - private SecurityEventCallback mEventCallback = new SecurityEventCallback(); + private SecurityEventCallback mEventCallback; private DevicePolicyManager mDpm; private Executor mExecutor; private DataAggregator mDataAggregator; @@ -42,9 +43,26 @@ public class SecurityLogSource implements DataSource { mDataAggregator = dataAggregator; mDpm = context.getSystemService(DevicePolicyManager.class); mExecutor = Executors.newSingleThreadExecutor(); + } + + @Override + public boolean initialize() { + // Confirm caller is system and the device is managed. Otherwise logs will + // be redacted. + try { + if (!mDpm.isDeviceManaged()) { + Slog.e(TAG, "Caller does not have device owner permissions"); + return false; + } + } catch (SecurityException e) { + Slog.e(TAG, "Security exception in initialize: ", e); + return false; + } mEventCallback = new SecurityEventCallback(); + return true; } + @Override @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void enable() { @@ -72,12 +90,8 @@ public class SecurityLogSource implements DataSource { } } - /** - * Check if security audit logging is enabled for the caller. - * - * @return Whether security audit logging is enabled. - */ - public boolean isAuditLogEnabled() { + @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + private boolean isAuditLogEnabled() { return mDpm.isAuditLogEnabled(); } diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java index 8a04ccf2dbc6..fec435154c83 100644 --- a/services/core/java/com/android/server/utils/WatchableImpl.java +++ b/services/core/java/com/android/server/utils/WatchableImpl.java @@ -33,6 +33,7 @@ public class WatchableImpl implements Watchable { /** * The list of observers. */ + @GuardedBy("mObservers") protected final ArrayList<Watcher> mObservers = new ArrayList<>(); /** @@ -83,7 +84,9 @@ public class WatchableImpl implements Watchable { * @return The number of registered observers. */ public int registeredObserverCount() { - return mObservers.size(); + synchronized (mObservers) { + return mObservers.size(); + } } /** diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 4b7e74af474c..dd769173fb34 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -25,7 +25,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFI import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; -import static com.android.internal.util.DumpUtils.dumpSparseArray; import static com.android.internal.util.DumpUtils.dumpSparseArrayValues; import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY; import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER; @@ -49,23 +48,15 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowTracingLegacy.WINSCOPE_EXT; import android.accessibilityservice.AccessibilityTrace; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Application; import android.content.Context; import android.content.pm.PackageManagerInternal; -import android.graphics.BLASTBufferQueue; -import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Insets; import android.graphics.Matrix; -import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PixelFormat; import android.graphics.Point; -import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -83,25 +74,16 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.TypedValue; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.MagnificationSpec; import android.view.Surface; -import android.view.Surface.OutOfResourcesException; -import android.view.SurfaceControl; import android.view.ViewConfiguration; import android.view.WindowInfo; import android.view.WindowManager; import android.view.WindowManager.TransitionFlags; import android.view.WindowManager.TransitionType; -import android.view.WindowManagerPolicyConstants; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.util.TraceBuffer; import com.android.internal.util.function.pooled.PooledLambda; @@ -111,7 +93,6 @@ import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; -import com.android.window.flags.Flags; import java.io.File; import java.io.IOException; @@ -301,36 +282,6 @@ final class AccessibilityController { } } - /** It is only used by unit test. */ - @VisibleForTesting - Surface forceShowMagnifierSurface(int displayId) { - final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); - if (displayMagnifier != null) { - displayMagnifier.mMagnifiedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport - .ViewportWindow.AnimationController.MAX_ALPHA); - return displayMagnifier.mMagnifiedViewport.mWindow.mSurface; - } - return null; - } - - void onWindowLayersChanged(int displayId) { - if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK - | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { - mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged", - FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, - "displayId=" + displayId); - } - final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); - if (displayMagnifier != null) { - displayMagnifier.onWindowLayersChanged(); - } - final WindowsForAccessibilityObserver windowsForA11yObserver = - mWindowsForAccessibilityObserver.get(displayId); - if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindows(); - } - } - void onDisplaySizeChanged(DisplayContent displayContent) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK @@ -563,9 +514,6 @@ final class AccessibilityController { } void dump(PrintWriter pw, String prefix) { - dumpSparseArray(pw, prefix, mDisplayMagnifiers, "magnification display", - (index, key) -> pw.printf("%sDisplay #%d:", prefix + " ", key), - dm -> dm.dump(pw, "")); dumpSparseArrayValues(pw, prefix, mWindowsForAccessibilityObserver, "windows for accessibility observer"); mAccessibilityWindowsPopulator.dump(pw, prefix); @@ -623,7 +571,6 @@ final class AccessibilityController { private final Context mDisplayContext; private final WindowManagerService mService; - private final MagnifiedViewport mMagnifiedViewport; private final Handler mHandler; private final DisplayContent mDisplayContent; private final Display mDisplay; @@ -660,8 +607,6 @@ final class AccessibilityController { mDisplay = display; mHandler = new MyHandler(mService.mH.getLooper()); mUserContextChangedNotifier = new UserContextChangedNotifier(mHandler); - mMagnifiedViewport = Flags.alwaysDrawMagnificationFullscreenBorder() - ? null : new MagnifiedViewport(); mAccessibilityTracing = AccessibilityController.getAccessibilityControllerInternal(mService); mLongAnimationDuration = mDisplayContext.getResources().getInteger( @@ -703,10 +648,6 @@ final class AccessibilityController { } else { mMagnificationSpec.clear(); } - - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.setShowMagnifiedBorderIfNeeded(); - } } void setFullscreenMagnificationActivated(boolean activated) { @@ -715,10 +656,6 @@ final class AccessibilityController { FLAGS_MAGNIFICATION_CALLBACK, "activated=" + activated); } mIsFullscreenMagnificationActivated = activated; - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.setMagnifiedRegionBorderShown(activated, true); - mMagnifiedViewport.showMagnificationBoundsIfNeeded(); - } } boolean isFullscreenMagnificationActivated() { @@ -729,18 +666,6 @@ final class AccessibilityController { return mIsFullscreenMagnificationActivated; } - void onWindowLayersChanged() { - if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { - mAccessibilityTracing.logTrace( - LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK); - } - if (DEBUG_LAYERS) { - Slog.i(LOG_TAG, "Layers changed."); - } - recomputeBounds(); - mService.scheduleAnimationLocked(); - } - void onDisplaySizeChanged(DisplayContent displayContent) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged", @@ -753,9 +678,6 @@ final class AccessibilityController { } recomputeBounds(); - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.onDisplaySizeChanged(); - } mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED); } @@ -926,10 +848,6 @@ final class AccessibilityController { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK); } - - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.destroyWindow(); - } } void recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded() { @@ -939,10 +857,6 @@ final class AccessibilityController { FLAGS_MAGNIFICATION_CALLBACK); } recomputeBounds(); - - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.drawWindowIfNeeded(); - } } void recomputeBounds() { @@ -1050,16 +964,9 @@ final class AccessibilityController { } visibleWindows.clear(); - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.intersectWithDrawBorderInset(screenWidth, screenHeight); - } - final boolean magnifiedChanged = !mOldMagnificationRegion.equals(mMagnificationRegion); if (magnifiedChanged) { - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.updateBorderDrawingStatus(screenWidth, screenHeight); - } mOldMagnificationRegion.set(mMagnificationRegion); final SomeArgs args = SomeArgs.obtain(); args.arg1 = Region.obtain(mMagnificationRegion); @@ -1139,420 +1046,11 @@ final class AccessibilityController { outSize.set(bounds.width(), bounds.height()); } - void dump(PrintWriter pw, String prefix) { - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.dump(pw, prefix); - } - } - - private final class MagnifiedViewport { - - private final float mBorderWidth; - private final int mHalfBorderWidth; - private final int mDrawBorderInset; - - @Nullable private final ViewportWindow mWindow; - - private boolean mFullRedrawNeeded; - - MagnifiedViewport() { - mBorderWidth = mDisplayContext.getResources().getDimension( - com.android.internal.R.dimen.accessibility_magnification_indicator_width); - mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2); - mDrawBorderInset = (int) mBorderWidth / 2; - mWindow = new ViewportWindow(mDisplayContext); - } - - void updateBorderDrawingStatus(int screenWidth, int screenHeight) { - mWindow.setBounds(mMagnificationRegion); - final Rect dirtyRect = mTempRect1; - if (mFullRedrawNeeded) { - mFullRedrawNeeded = false; - dirtyRect.set(mDrawBorderInset, mDrawBorderInset, - screenWidth - mDrawBorderInset, - screenHeight - mDrawBorderInset); - mWindow.invalidate(dirtyRect); - } else { - final Region dirtyRegion = mTempRegion3; - dirtyRegion.set(mMagnificationRegion); - dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR); - dirtyRegion.getBounds(dirtyRect); - mWindow.invalidate(dirtyRect); - } - } - - void setShowMagnifiedBorderIfNeeded() { - // If this message is pending, we are in a rotation animation and do not want - // to show the border. We will do so when the pending message is handled. - if (!mHandler.hasMessages( - MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { - setMagnifiedRegionBorderShown( - isFullscreenMagnificationActivated(), true); - } - } - - // Can be called outside of a surface transaction - void showMagnificationBoundsIfNeeded() { - if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { - mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded", - FLAGS_MAGNIFICATION_CALLBACK); - } - mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) - .sendToTarget(); - } - - void intersectWithDrawBorderInset(int screenWidth, int screenHeight) { - mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset, - screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset, - Region.Op.INTERSECT); - } - - void onDisplaySizeChanged() { - // If fullscreen magnification is activated, hide the border immediately so - // the user does not see strange artifacts during display size changed caused by - // rotation or folding/unfolding the device. In the rotation case, the - // screenshot used for rotation already has the border. After the rotation is - // completed we will show the border. - if (isFullscreenMagnificationActivated()) { - setMagnifiedRegionBorderShown(false, false); - final long delay = (long) (mLongAnimationDuration - * mService.getWindowAnimationScaleLocked()); - Message message = mHandler.obtainMessage( - MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); - mHandler.sendMessageDelayed(message, delay); - } - mWindow.updateSize(); - } - - void setMagnifiedRegionBorderShown(boolean shown, boolean animate) { - if (mWindow.setShown(shown, animate)) { - mFullRedrawNeeded = true; - // Clear the old region, so recomputeBounds will refresh the current region. - mOldMagnificationRegion.set(0, 0, 0, 0); - } - } - - void drawWindowIfNeeded() { - mWindow.postDrawIfNeeded(); - } - - void destroyWindow() { - mWindow.releaseSurface(); - } - - void dump(PrintWriter pw, String prefix) { - mWindow.dump(pw, prefix); - } - - // TODO(291891390): Remove this class when we clean up the flag - // alwaysDrawMagnificationFullscreenBorder - private final class ViewportWindow implements Runnable { - private static final String SURFACE_TITLE = "Magnification Overlay"; - - private final Region mBounds = new Region(); - private final Rect mDirtyRect = new Rect(); - private final Paint mPaint = new Paint(); - - private final SurfaceControl mSurfaceControl; - /** After initialization, it should only be accessed from animation thread. */ - private final SurfaceControl.Transaction mTransaction; - private final BLASTBufferQueue mBlastBufferQueue; - private final Surface mSurface; - - private final AnimationController mAnimationController; - - private boolean mShown; - private boolean mLastSurfaceShown; - private int mAlpha; - private int mPreviousAlpha; - - private volatile boolean mInvalidated; - - ViewportWindow(Context context) { - SurfaceControl surfaceControl = null; - try { - surfaceControl = mDisplayContent - .makeOverlay() - .setName(SURFACE_TITLE) - .setBLASTLayer() - .setFormat(PixelFormat.TRANSLUCENT) - .setCallsite("ViewportWindow") - .build(); - } catch (OutOfResourcesException oore) { - /* ignore */ - } - mSurfaceControl = surfaceControl; - mDisplay.getRealSize(mScreenSize); - mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl, - mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888); - - final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); - final int layer = - mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) * - WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; - t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0); - InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t, - mDisplayContent.getDisplayId(), "Magnification Overlay"); - t.apply(); - mTransaction = t; - mSurface = mBlastBufferQueue.createSurface(); - - mAnimationController = new AnimationController(context, - mService.mH.getLooper()); - - TypedValue typedValue = new TypedValue(); - context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight, - typedValue, true); - final int borderColor = context.getColor(typedValue.resourceId); - - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setStrokeWidth(mBorderWidth); - mPaint.setColor(borderColor); - - mInvalidated = true; - } - - /** Returns {@code true} if the state is changed to shown. */ - boolean setShown(boolean shown, boolean animate) { - synchronized (mService.mGlobalLock) { - if (mShown == shown) { - return false; - } - mShown = shown; - mAnimationController.onFrameShownStateChanged(shown, animate); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); - } - } - return shown; - } - - @SuppressWarnings("unused") - // Called reflectively from an animator. - int getAlpha() { - synchronized (mService.mGlobalLock) { - return mAlpha; - } - } - - void setAlpha(int alpha) { - synchronized (mService.mGlobalLock) { - if (mAlpha == alpha) { - return; - } - mAlpha = alpha; - invalidate(null); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha); - } - } - } - - void setBounds(Region bounds) { - synchronized (mService.mGlobalLock) { - if (mBounds.equals(bounds)) { - return; - } - mBounds.set(bounds); - invalidate(mDirtyRect); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds); - } - } - } - - void updateSize() { - synchronized (mService.mGlobalLock) { - getDisplaySizeLocked(mScreenSize); - mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y, - PixelFormat.RGBA_8888); - invalidate(mDirtyRect); - } - } - - void invalidate(Rect dirtyRect) { - if (dirtyRect != null) { - mDirtyRect.set(dirtyRect); - } else { - mDirtyRect.setEmpty(); - } - mInvalidated = true; - mService.scheduleAnimationLocked(); - } - - void postDrawIfNeeded() { - if (mInvalidated) { - mService.mAnimationHandler.post(this); - } - } - - @Override - public void run() { - drawOrRemoveIfNeeded(); - } - - /** - * This method must only be called by animation handler directly to make sure - * thread safe and there is no lock held outside. - */ - private void drawOrRemoveIfNeeded() { - // Drawing variables (alpha, dirty rect, and bounds) access is synchronized - // using WindowManagerGlobalLock. Grab copies of these values before - // drawing on the canvas so that drawing can be performed outside of the lock. - int alpha; - boolean redrawBounds; - Rect drawingRect = null; - Region drawingBounds = null; - synchronized (mService.mGlobalLock) { - if (mBlastBufferQueue.mNativeObject == 0) { - // Complete removal since releaseSurface has been called. - if (mSurface.isValid()) { - mTransaction.remove(mSurfaceControl).apply(); - mSurface.release(); - } - return; - } - if (!mInvalidated) { - return; - } - mInvalidated = false; - - alpha = mAlpha; - // For b/325863281, we should ensure the drawn border path is cleared when - // alpha = 0. Therefore, we cache the last used alpha when drawing as - // mPreviousAlpha and check it here. If mPreviousAlpha > 0, which means - // the border is showing now, then we should still redraw the clear path - // on the canvas so the border is cleared. - redrawBounds = mAlpha > 0 || mPreviousAlpha > 0; - if (redrawBounds) { - drawingBounds = new Region(mBounds); - // Empty dirty rectangle means unspecified. - if (mDirtyRect.isEmpty()) { - mBounds.getBounds(mDirtyRect); - } - mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth); - drawingRect = new Rect(mDirtyRect); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow bounds: " + mBounds); - Slog.i(LOG_TAG, "ViewportWindow dirty rect: " + mDirtyRect); - } - } - } - - final boolean showSurface; - // Draw without holding WindowManagerGlobalLock. - if (redrawBounds) { - Canvas canvas = null; - try { - canvas = mSurface.lockCanvas(drawingRect); - } catch (IllegalArgumentException | OutOfResourcesException e) { - /* ignore */ - } - if (canvas == null) { - return; - } - canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); - mPaint.setAlpha(alpha); - canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint); - mSurface.unlockCanvasAndPost(canvas); - mPreviousAlpha = alpha; - } - - showSurface = alpha > 0; - - if (showSurface && !mLastSurfaceShown) { - mTransaction.show(mSurfaceControl).apply(); - mLastSurfaceShown = true; - } else if (!showSurface && mLastSurfaceShown) { - mTransaction.hide(mSurfaceControl).apply(); - mLastSurfaceShown = false; - } - } - - @GuardedBy("mService.mGlobalLock") - void releaseSurface() { - mBlastBufferQueue.destroy(); - // Post to perform cleanup on the thread which handles mSurface. - mService.mAnimationHandler.post(this); - } - - void dump(PrintWriter pw, String prefix) { - pw.println(prefix - + " mBounds= " + mBounds - + " mDirtyRect= " + mDirtyRect - + " mWidth= " + mScreenSize.x - + " mHeight= " + mScreenSize.y); - } - - private final class AnimationController extends Handler { - private static final String PROPERTY_NAME_ALPHA = "alpha"; - - private static final int MIN_ALPHA = 0; - private static final int MAX_ALPHA = 255; - - private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1; - - private final ValueAnimator mShowHideFrameAnimator; - - AnimationController(Context context, Looper looper) { - super(looper); - mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this, - PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA); - - Interpolator interpolator = new DecelerateInterpolator(2.5f); - final long longAnimationDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_longAnimTime); - - mShowHideFrameAnimator.setInterpolator(interpolator); - mShowHideFrameAnimator.setDuration(longAnimationDuration); - } - - void onFrameShownStateChanged(boolean shown, boolean animate) { - obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED, - shown ? 1 : 0, animate ? 1 : 0).sendToTarget(); - } - - @Override - public void handleMessage(Message message) { - switch (message.what) { - case MSG_FRAME_SHOWN_STATE_CHANGED: { - final boolean shown = message.arg1 == 1; - final boolean animate = message.arg2 == 1; - - if (animate) { - if (mShowHideFrameAnimator.isRunning()) { - mShowHideFrameAnimator.reverse(); - } else { - if (shown) { - mShowHideFrameAnimator.start(); - } else { - mShowHideFrameAnimator.reverse(); - } - } - } else { - mShowHideFrameAnimator.cancel(); - if (shown) { - setAlpha(MAX_ALPHA); - } else { - setAlpha(MIN_ALPHA); - } - } - } break; - } - } - } - } - } - private class MyHandler extends Handler { public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1; public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4; - - // TODO(291891390): Remove this field when we clean up the flag - // alwaysDrawMagnificationFullscreenBorder - public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; - public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6; + public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 5; MyHandler(Looper looper) { super(looper); @@ -1576,17 +1074,6 @@ final class AccessibilityController { mCallbacks.onDisplaySizeChanged(); } break; - case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { - synchronized (mService.mGlobalLock) { - if (isFullscreenMagnificationActivated()) { - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.setMagnifiedRegionBorderShown(true, true); - } - mService.scheduleAnimationLocked(); - } - } - } break; - case MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED: { final boolean shown = message.arg1 == 1; mCallbacks.onImeWindowVisibilityChanged(shown); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d2546e49267a..ef33ffe509b3 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3212,18 +3212,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * will be ignored. */ boolean isUniversalResizeable() { - if (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME) { - return false; - } - final boolean compatEnabled = Flags.universalResizableByDefault() - && mDisplayContent != null && mDisplayContent.getConfiguration() - .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP - && mDisplayContent.getIgnoreOrientationRequest() - && info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT); - if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) { - return false; - } - if (mWmService.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(packageName)) { + final boolean isLargeScreen = mDisplayContent != null && mDisplayContent.getConfiguration() + .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP + && mDisplayContent.getIgnoreOrientationRequest(); + if (!canBeUniversalResizeable(info.applicationInfo, mWmService, isLargeScreen, + true /* forActivity */)) { return false; } if (mAppCompatController.mAllowRestrictedResizability.getAsBoolean()) { @@ -3234,6 +3227,31 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A .userPreferenceCompatibleWithNonResizability(); } + /** + * Returns {@code true} if the fixed orientation, aspect ratio, resizability of the application + * can be ignored. + */ + static boolean canBeUniversalResizeable(ApplicationInfo appInfo, WindowManagerService wms, + boolean isLargeScreen, boolean forActivity) { + if (appInfo.category == ApplicationInfo.CATEGORY_GAME) { + return false; + } + final boolean compatEnabled = isLargeScreen && Flags.universalResizableByDefault() + && appInfo.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT); + if (!compatEnabled && !wms.mConstants.mIgnoreActivityOrientationRequest) { + return false; + } + if (wms.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(appInfo.packageName)) { + return false; + } + if (forActivity) { + // The caller will check both application and activity level property. + return true; + } + return !AppCompatController.allowRestrictedResizability(wms.mContext.getPackageManager(), + appInfo.packageName); + } + boolean isResizeable() { return mAtmService.mForceResizableActivities || ActivityInfo.isResizeableMode(info.resizeMode) @@ -3667,16 +3685,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pauseKeyDispatchingLocked(); - // We are finishing the top focused activity and its task has nothing to be focused so - // the next focusable task should be focused. - if (mayAdjustTop && task.topRunningActivity(true /* focusableOnly */) - == null) { - task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */, - shouldAdjustGlobalFocus); - } - - finishActivityResults(resultCode, resultData, resultGrants); - final boolean endTask = task.getTopNonFinishingActivity() == null && !task.isClearingToReuseTask(); final WindowContainer<?> trigger = endTask ? task : this; @@ -3687,6 +3695,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (transition != null) { transition.collectClose(trigger); } + // We are finishing the top focused activity and its task has nothing to be focused so + // the next focusable task should be focused. + if (mayAdjustTop && task.topRunningActivity(true /* focusableOnly */) + == null) { + task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */, + shouldAdjustGlobalFocus); + } + + finishActivityResults(resultCode, resultData, resultGrants); + if (isState(RESUMED)) { if (endTask) { mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted( diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java index 203932d4df58..330283fb9ab3 100644 --- a/services/core/java/com/android/server/wm/AppCompatController.java +++ b/services/core/java/com/android/server/wm/AppCompatController.java @@ -77,13 +77,8 @@ class AppCompatController { mAppCompatOverrides); mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> { // Application level. - try { - if (packageManager.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, - mActivityRecord.packageName).getBoolean()) { - return true; - } - } catch (PackageManager.NameNotFoundException e) { - // Fall through. + if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) { + return true; } // Activity level. try { @@ -98,6 +93,15 @@ class AppCompatController { }); } + static boolean allowRestrictedResizability(PackageManager pm, String packageName) { + try { + return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName) + .getBoolean(); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + @NonNull TransparentPolicy getTransparentPolicy() { return mTransparentPolicy; diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 49f717e228d2..48e1c069821c 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -346,12 +346,15 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + // not all virtual displays have an ImeInsetsSourceProvider, so it is not + // guaranteed that the IME will be started when the control target reports its + // requested visibility back. Thus, invoking the listener here. + invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken); } else { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); } } - invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken); } // TODO(b/353463205) check callers to see if we can make statsToken @NonNull diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS index 2401f9072ea3..c7667b40ddd9 100644 --- a/services/core/java/com/android/server/wm/OWNERS +++ b/services/core/java/com/android/server/wm/OWNERS @@ -19,6 +19,7 @@ yunfanc@google.com wilsonshih@google.com jiamingliu@google.com pdwilliams@google.com +charlesccchen@google.com # Files related to background activity launches per-file Background*Start* = set noparent diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index cb333f02757e..1c8d06e52329 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -152,19 +152,20 @@ final class DevicePolicyEngine { mAdminPolicySize = new SparseArray<>(); } - private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) { + private void forceEnforcementRefreshIfUserRestrictionLocked( + @NonNull PolicyDefinition<?> policyDefinition) { try { - if (shouldForceEnforcementRefresh(policyDefinition)) { + if (isUserRestrictionPolicy(policyDefinition)) { // This is okay because it's only true for user restrictions which are all <Boolean> forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition); } } catch (Throwable e) { // Catch any possible exceptions just to be on the safe side - Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e); + Log.e(TAG, "Exception thrown during forceEnforcementRefreshIfUserRestrictionLocked", e); } } - private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) { + private boolean isUserRestrictionPolicy(@NonNull PolicyDefinition<?> policyDefinition) { // These are all "not nullable" but for the purposes of maximum safety for a lightly tested // change we check here if (policyDefinition == null) { @@ -257,7 +258,7 @@ final class DevicePolicyEngine { // No need to notify admins as no new policy is actually enforced, we're just filling in // the data structures. if (!skipEnforcePolicy) { - maybeForceEnforcementRefreshLocked(policyDefinition); + forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition); if (policyChanged) { onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId); } @@ -347,7 +348,7 @@ final class DevicePolicyEngine { Objects.requireNonNull(enforcingAdmin); synchronized (mLock) { - maybeForceEnforcementRefreshLocked(policyDefinition); + forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition); if (!hasLocalPolicyLocked(policyDefinition, userId)) { return; } @@ -517,7 +518,7 @@ final class DevicePolicyEngine { // No need to notify admins as no new policy is actually enforced, we're just filling in // the data structures. if (!skipEnforcePolicy) { - maybeForceEnforcementRefreshLocked(policyDefinition); + forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition); if (policyChanged) { onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } @@ -570,7 +571,7 @@ final class DevicePolicyEngine { boolean policyChanged = policyState.removePolicy(enforcingAdmin); - maybeForceEnforcementRefreshLocked(policyDefinition); + forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition); if (policyChanged) { onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index dde213de1d40..6f0d26a2426d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -23178,6 +23178,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); + if (Flags.lockNowCoexistence()) { + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK, + MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); + } CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); @@ -23252,8 +23256,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCATION, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + if (!Flags.lockNowCoexistence()) { + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK, + 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, @@ -23708,24 +23714,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public void setMtePolicy(int flags, String callerPackageName) { - final Set<Integer> allowedModes = - Set.of( - DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY, - DevicePolicyManager.MTE_DISABLED, - DevicePolicyManager.MTE_ENABLED); - Preconditions.checkArgument( - allowedModes.contains(flags), "Provided mode is not one of the allowed values."); - // In general, this API should be available when "bootctl_settings_toggle" is set, which - // signals that there is a control for MTE in the user settings and this API fundamentally - // is a way for the device admin to override that setting. - // Allow bootctl_device_policy_manager as an override, e.g. to offer the - // DevicePolicyManager only without a visible user setting. - if (!mInjector.systemPropertiesGetBoolean( - "ro.arm64.memtag.bootctl_device_policy_manager", - mInjector.systemPropertiesGetBoolean( - "ro.arm64.memtag.bootctl_settings_toggle", false))) { - throw new UnsupportedOperationException("device does not support MTE"); - } + checkMteSupportedAndAllowedPolicy(flags); final CallerIdentity caller = getCallerIdentity(callerPackageName); // For now we continue to restrict the DISABLED setting to device owner - we might need // another permission for this in future. @@ -23783,6 +23772,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public void setMtePolicyBySystem( + @NonNull String systemEntity, int policy) { + Objects.requireNonNull(systemEntity); + checkMteSupportedAndAllowedPolicy(policy); + + Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), + "Only system services can call setMtePolicyBySystem"); + + if (!Flags.setMtePolicyCoexistence()) { + throw new UnsupportedOperationException("System can not set MTE policy only"); + } + + EnforcingAdmin admin = EnforcingAdmin.createSystemEnforcingAdmin(systemEntity); + if (policy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) { + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.MEMORY_TAGGING, + admin, + new IntegerPolicyValue(policy)); + } else { + mDevicePolicyEngine.removeGlobalPolicy( + PolicyDefinition.MEMORY_TAGGING, + admin); + } + } + + private void checkMteSupportedAndAllowedPolicy(int policy) { + final Set<Integer> allowedModes = + Set.of( + DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY, + DevicePolicyManager.MTE_DISABLED, + DevicePolicyManager.MTE_ENABLED); + Preconditions.checkArgument( + allowedModes.contains(policy), "Provided mode is not one of the allowed values."); + // In general, this API should be available when "bootctl_settings_toggle" is set, which + // signals that there is a control for MTE in the user settings and this API fundamentally + // is a way for the device admin to override that setting. + // Allow bootctl_device_policy_manager as an override, e.g. to offer the + // DevicePolicyManager only without a visible user setting. + if (!mInjector.systemPropertiesGetBoolean( + "ro.arm64.memtag.bootctl_device_policy_manager", + mInjector.systemPropertiesGetBoolean( + "ro.arm64.memtag.bootctl_settings_toggle", false))) { + throw new UnsupportedOperationException("device does not support MTE"); + } + } + + @Override public int getMtePolicy(String callerPackageName) { final CallerIdentity caller = getCallerIdentity(callerPackageName); if (Flags.setMtePolicyCoexistence()) { @@ -24161,6 +24197,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String supervisionBackupId = "36.2.supervision-support"; boolean supervisionMigrated = maybeMigrateResetPasswordTokenLocked(supervisionBackupId); supervisionMigrated |= maybeMigrateSuspendedPackagesLocked(supervisionBackupId); + supervisionMigrated |= maybeMigrateSetKeyguardDisabledFeatures(supervisionBackupId); if (supervisionMigrated) { Slogf.i(LOG_TAG, "Backup made: " + supervisionBackupId); } @@ -24174,6 +24211,38 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Additional migration steps should repeat the pattern above with a new backupId. } + @GuardedBy("getLockObject()") + private boolean maybeMigrateSetKeyguardDisabledFeatures(String backupId) { + Slog.i(LOG_TAG, "Migrating set keyguard disabled features to policy engine"); + if (!Flags.setKeyguardDisabledFeaturesCoexistence()) { + return false; + } + if (mOwners.isSetKeyguardDisabledFeaturesMigrated()) { + return false; + } + // Create backup if none exists + mDevicePolicyEngine.createBackup(backupId); + try { + iterateThroughDpcAdminsLocked((admin, enforcingAdmin) -> { + if (admin.disabledKeyguardFeatures == 0) { + return; + } + int userId = enforcingAdmin.getUserId(); + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.KEYGUARD_DISABLED_FEATURES, + enforcingAdmin, + new IntegerPolicyValue(admin.disabledKeyguardFeatures), + userId); + }); + } catch (Exception e) { + Slog.wtf(LOG_TAG, "Failed to migrate set keyguard disabled to policy engine", e); + } + + Slog.i(LOG_TAG, "Marking set keyguard disabled features migration complete"); + mOwners.markSetKeyguardDisabledFeaturesMigrated(); + return true; + } + private void migratePermissionGrantStatePolicies() { Slogf.i(LOG_TAG, "Migrating PERMISSION_GRANT policy to device policy engine."); for (UserInfo userInfo : mUserManager.getUsers()) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index be4eea42f09e..1c75f2f39c80 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -695,6 +695,19 @@ class Owners { } } + void markSetKeyguardDisabledFeaturesMigrated() { + synchronized (mData) { + mData.mSetKeyguardDisabledFeaturesMigrated = true; + mData.writeDeviceOwner(); + } + } + + boolean isSetKeyguardDisabledFeaturesMigrated() { + synchronized (mData) { + return mData.mSetKeyguardDisabledFeaturesMigrated; + } + } + @GuardedBy("mData") void pushToAppOpsLocked() { if (!mSystemReady) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java index 1cae924a8cd1..caaf0964bb4e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java @@ -95,6 +95,8 @@ class OwnersData { "resetPasswordWithTokenMigrated"; private static final String ATTR_MEMORY_TAGGING_MIGRATED = "memoryTaggingMigrated"; + private static final String ATTR_SET_KEYGUARD_DISABLED_FEATURES_MIGRATED = + "setKeyguardDisabledFeaturesMigrated"; private static final String ATTR_MIGRATED_POST_UPGRADE = "migratedPostUpgrade"; @@ -129,6 +131,7 @@ class OwnersData { boolean mSuspendedPackagesMigrated = false; boolean mResetPasswordWithTokenMigrated = false; boolean mMemoryTaggingMigrated = false; + boolean mSetKeyguardDisabledFeaturesMigrated = false; boolean mPoliciesMigratedPostUpdate = false; @@ -434,6 +437,10 @@ class OwnersData { out.attributeBoolean(null, ATTR_MEMORY_TAGGING_MIGRATED, mMemoryTaggingMigrated); } + if (Flags.setKeyguardDisabledFeaturesCoexistence()) { + out.attributeBoolean(null, ATTR_SET_KEYGUARD_DISABLED_FEATURES_MIGRATED, + mSetKeyguardDisabledFeaturesMigrated); + } out.endTag(null, TAG_POLICY_ENGINE_MIGRATION); } @@ -510,6 +517,10 @@ class OwnersData { mMemoryTaggingMigrated = Flags.setMtePolicyCoexistence() && parser.getAttributeBoolean(null, ATTR_MEMORY_TAGGING_MIGRATED, false); + mSetKeyguardDisabledFeaturesMigrated = + Flags.setKeyguardDisabledFeaturesCoexistence() + && parser.getAttributeBoolean(null, + ATTR_SET_KEYGUARD_DISABLED_FEATURES_MIGRATED, false); break; default: Slog.e(TAG, "Unexpected tag: " + tag); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 82d49fcac305..112414e6c4df 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -422,6 +422,8 @@ public final class SystemServer implements Dumpable { "com.android.server.wifi.aware.WifiAwareService"; private static final String WIFI_P2P_SERVICE_CLASS = "com.android.server.wifi.p2p.WifiP2pService"; + private static final String WIFI_USD_SERVICE_CLASS = + "com.android.server.wifi.usd.UsdService"; private static final String CONNECTIVITY_SERVICE_APEX_PATH = "/apex/com.android.tethering/javalib/service-connectivity.jar"; private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = @@ -2145,6 +2147,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startServiceFromJar( WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); + // Start USD service + if (android.net.wifi.flags.Flags.usd()) { + t.traceBegin("StartUsd"); + mSystemServiceManager.startServiceFromJar( + WIFI_USD_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } } if (context.getPackageManager().hasSystemFeature( diff --git a/services/manifest_services.xml b/services/manifest_services.xml index 114fe324f016..945720544991 100644 --- a/services/manifest_services.xml +++ b/services/manifest_services.xml @@ -4,4 +4,9 @@ <version>2</version> <fqname>IAltitudeService/default</fqname> </hal> + <hal format="aidl"> + <name>android.frameworks.devicestate</name> + <version>1</version> + <fqname>IDeviceStateService/default</fqname> + </hal> </manifest> diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt index 3fdb53f5ab59..31f03704a756 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -289,6 +289,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag AndroidPackage::getEmergencyInstaller, AndroidPackage::isAllowCrossUidActivitySwitchFromBelow, AndroidPackage::getIntentMatchingFlags, + AndroidPackage::getPageSizeAppCompatFlags, ) override fun extraParams() = listOf( diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 993569fb17fe..0d25426700a6 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -392,3 +392,10 @@ test_module_config { ], include_filters: ["com.android.server.StorageManagerServiceTest"], } + +test_module_config { + name: "FrameworksMockingServicesTests_service_batteryServiceTest", + base: "FrameworksMockingServicesTests", + test_suites: ["device-tests"], + include_filters: ["com.android.server.BatteryServiceTest"], +} diff --git a/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java index 5e2f80bf8311..1fbd53a27a4f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java @@ -79,6 +79,8 @@ public class BatteryServiceTest { private static final int UPDATED_BATTERY_HEALTH = 3; private static final int CURRENT_CHARGE_COUNTER = 4680000; private static final int UPDATED_CHARGE_COUNTER = 4218000; + private static final int CURRENT_MAX_CHARGING_CURRENT = 298125; + private static final int UPDATED_MAX_CHARGING_CURRENT = 398125; private static final int HANDLER_IDLE_TIME_MS = 5000; @Rule public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this) @@ -143,7 +145,7 @@ public class BatteryServiceTest { @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) public void onlyVoltageUpdated_lessThenOnePercent_broadcastNotSent() { mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -156,7 +158,8 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, CURRENT_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, + CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -165,13 +168,17 @@ public class BatteryServiceTest { @Test @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) - public void onlyVoltageUpdated_broadcastSent() { + public void voltageUpdated_withUpdateInChargingCurrent_broadcastSent() { mBatteryService.mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime() - 20000; + long lastChargingCurrentUpdateTime = + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime; mBatteryService.update(createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); + assertTrue(lastChargingCurrentUpdateTime + < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @@ -180,7 +187,8 @@ public class BatteryServiceTest { public void onlyTempUpdated_lessThenOneDegreeCelsius_broadcastNotSent() { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, + CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -191,23 +199,31 @@ public class BatteryServiceTest { @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) public void tempUpdated_broadcastSent() { long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime; + long lastChargingCurrentUpdateTime = + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime; mBatteryService.update( createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, TEMP_MORE_THEN_ONE_DEGREE_CELSIUS, - UPDATED_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + UPDATED_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, + UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime); + assertTrue(lastChargingCurrentUpdateTime + < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @Test @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) - public void batteryHealthUpdated_voltageAndTempConst_broadcastSent() { + public void batteryHealthUpdated_withOtherExtrasConstant_broadcastSent() { + long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime; + long lastChargingCurrentUpdateTime = + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime; mBatteryService.update( - createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, + createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, CURRENT_CHARGE_COUNTER, - UPDATED_BATTERY_HEALTH)); + UPDATED_BATTERY_HEALTH, UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -217,10 +233,13 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, UPDATED_CHARGE_COUNTER, - UPDATED_BATTERY_HEALTH)); + UPDATED_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); + assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime); + assertTrue(lastChargingCurrentUpdateTime + < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @@ -228,7 +247,7 @@ public class BatteryServiceTest { @DisableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) public void voltageUpdated_lessThanOnePercent_flagDisabled_broadcastSent() { mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -241,7 +260,7 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, UPDATED_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -254,7 +273,7 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS, UPDATED_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -267,10 +286,42 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, UPDATED_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); + + waitForHandlerToExecute(); + + verifyNumberOfTimesBroadcastSent(1); + } + + @Test + @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) + public void onlyMaxChargingCurrentUpdated_beforeFiveSeconds_broadcastNotSent() { + mBatteryService.update( + createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, + CURRENT_CHARGE_COUNTER, + CURRENT_BATTERY_HEALTH, + UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); + verifyNumberOfTimesBroadcastSent(0); + } + + @Test + @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) + public void maxChargingCurrentUpdated_afterFiveSeconds_broadcastSent() { + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime = + SystemClock.elapsedRealtime() - 5000; + long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime; + mBatteryService.update( + createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, + CURRENT_CHARGE_COUNTER, + CURRENT_BATTERY_HEALTH, + UPDATED_MAX_CHARGING_CURRENT)); + + waitForHandlerToExecute(); + + assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @@ -278,7 +329,8 @@ public class BatteryServiceTest { int batteryVoltage, int batteryTemperature, int batteryChargeCounter, - int batteryHealth) { + int batteryHealth, + int maxChargingCurrent) { HealthInfo h = new HealthInfo(); h.batteryVoltageMillivolts = batteryVoltage; h.batteryTemperatureTenthsCelsius = batteryTemperature; @@ -287,7 +339,7 @@ public class BatteryServiceTest { h.batteryHealth = batteryHealth; h.batteryPresent = true; h.batteryLevel = 100; - h.maxChargingCurrentMicroamps = 298125; + h.maxChargingCurrentMicroamps = maxChargingCurrent; h.batteryCurrentAverageMicroamps = -2812; h.batteryCurrentMicroamps = 298125; h.maxChargingVoltageMicrovolts = 3000; @@ -308,7 +360,8 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, CURRENT_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, + CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); } 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 ace6aae1a8fc..d9332ec05697 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -47,8 +47,10 @@ import static com.android.server.am.Flags.FLAG_AVOID_RESOLVING_TYPE; import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK; import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE; import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -105,6 +107,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IProgressListener; +import android.os.IpcDataCache; import android.os.Looper; import android.os.Message; import android.os.Parcel; @@ -959,28 +962,37 @@ public class ActivityManagerServiceTest { @Test @SuppressWarnings("GuardedBy") public void testBroadcastStickyIntent_verifyTypeNotResolved() throws Exception { - final Intent intent = new Intent(TEST_ACTION1); - final Uri uri = new Uri.Builder() - .scheme(SCHEME_CONTENT) - .authority(TEST_AUTHORITY) - .path("green") - .build(); - intent.setData(uri); - broadcastIntent(intent, null, true, TEST_MIME_TYPE, USER_ALL); - assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, USER_ALL), - StickyBroadcast.create(intent, false, Process.myUid(), PROCESS_STATE_UNKNOWN, - TEST_MIME_TYPE)); - when(mContentResolver.getType(uri)).thenReturn(TEST_MIME_TYPE); + MockitoSession mockitoSession = + ExtendedMockito.mockitoSession().mockStatic(IpcDataCache.class).startMocking(); - addUidRecord(TEST_UID, TEST_PACKAGE); - final ProcessRecord procRecord = mAms.getProcessRecordLocked(TEST_PACKAGE, TEST_UID); - final IntentFilter intentFilter = new IntentFilter(TEST_ACTION1); - intentFilter.addDataType(TEST_MIME_TYPE); - final Intent resultIntent = mAms.registerReceiverWithFeature(procRecord.getThread(), - TEST_PACKAGE, null, null, null, intentFilter, null, TEST_USER, - Context.RECEIVER_EXPORTED); - assertNotNull(resultIntent); - verify(mContentResolver, never()).getType(any()); + try { + final Intent intent = new Intent(TEST_ACTION1); + final Uri uri = new Uri.Builder() + .scheme(SCHEME_CONTENT) + .authority(TEST_AUTHORITY) + .path("green") + .build(); + intent.setData(uri); + broadcastIntent(intent, null, true, TEST_MIME_TYPE, USER_ALL); + assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, USER_ALL), + StickyBroadcast.create(intent, false, Process.myUid(), PROCESS_STATE_UNKNOWN, + TEST_MIME_TYPE)); + when(mContentResolver.getType(uri)).thenReturn(TEST_MIME_TYPE); + ExtendedMockito.doNothing().when( + () -> IpcDataCache.invalidateCache(anyString(), anyString())); + + addUidRecord(TEST_UID, TEST_PACKAGE); + final ProcessRecord procRecord = mAms.getProcessRecordLocked(TEST_PACKAGE, TEST_UID); + final IntentFilter intentFilter = new IntentFilter(TEST_ACTION1); + intentFilter.addDataType(TEST_MIME_TYPE); + final Intent resultIntent = mAms.registerReceiverWithFeature(procRecord.getThread(), + TEST_PACKAGE, null, null, null, intentFilter, null, TEST_USER, + Context.RECEIVER_EXPORTED); + assertNotNull(resultIntent); + verify(mContentResolver, never()).getType(any()); + } finally { + mockitoSession.finishMocking(); + } } @SuppressWarnings("GuardedBy") diff --git a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java index c77ab0f303d9..745424836faa 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java @@ -21,10 +21,14 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static org.mockito.Mockito.verify; + import android.content.ContentResolver; import android.os.SystemProperties; +import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; +import android.util.proto.ProtoOutputStream; import com.android.dx.mockito.inline.extended.ExtendedMockito; @@ -34,6 +38,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Answers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; @@ -111,6 +116,20 @@ public class SettingsToPropertiesMapperTest { } @Test + public void testClearAconfigStorageOverride() { + SettingsToPropertiesMapper spyMapper = Mockito.spy(new SettingsToPropertiesMapper( + mMockContentResolver, TEST_MAPPING, new String[] {}, new String[] {})); + HashMap flags = new HashMap(); + flags.put("test_package.test_flag", null); + DeviceConfig.Properties props = new DeviceConfig.Properties("test_namespace", flags); + + spyMapper.setLocalOverridesInNewStorage(props); + + verify(spyMapper).writeFlagOverrideRemovalRequest(new ProtoOutputStream(), + "test_package", "test_flag", true); + } + + @Test public void validateRegisteredGlobalSettings() { HashSet<String> hashSet = new HashSet<>(); for (String globalSetting : SettingsToPropertiesMapper.sGlobalSettings) { 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 dd7ce21e3628..c831475577d8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.job; +import static android.app.job.Flags.FLAG_HANDLE_ABANDONED_JOBS; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -82,6 +83,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.WorkSource; import android.os.WorkSource.WorkChain; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; @@ -1056,6 +1058,75 @@ public class JobSchedulerServiceTest { /** * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} + * returns a job with the correct delay for abandoned jobs. + */ + @Test + @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS) + public void testGetRescheduleJobForFailure_abandonedJob() { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final long initialBackoffMs = MINUTE_IN_MILLIS; + mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3; + + JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", + createJobInfo() + .setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR)); + assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed()); + + // failure = 1, systemStop = 0, abandoned = 1 + JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 2, systemstop = 0, abandoned = 2 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals(nowElapsed + (2 * initialBackoffMs), rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 3, systemstop = 0, abandoned = 3 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals(nowElapsed + (3 * initialBackoffMs), rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 4, systemstop = 0, abandoned = 4 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals( + nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 3)), + rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 4, systemstop = 1, abandoned = 4 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); + assertEquals( + nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 3)), + rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 4, systemStop = 4 / SYSTEM_STOP_TO_FAILURE_RATIO, abandoned = 4 + for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) { + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_SYSTEM_PROCESSING, + JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED); + } + assertEquals( + nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 4)), + rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that + * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} * returns a job that is correctly marked as demoted by the user. */ @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java index c6a6865f1cf1..c64973a67589 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java @@ -706,14 +706,14 @@ public class FlexibilityControllerTest { // "True" start is nowElapsed + HOUR_IN_MILLIS nowElapsed + HOUR_IN_MILLIS + adjustmentMs, nowElapsed + 2 * HOUR_IN_MILLIS, - 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); jsFlex = new JobStatus(jsFlex, // "True" start is nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + adjustmentMs, nowElapsed + 2 * HOUR_IN_MILLIS, - 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); @@ -726,13 +726,13 @@ public class FlexibilityControllerTest { jsBasic = new JobStatus(jsBasic, nowElapsed + 30 * MINUTE_IN_MILLIS, NO_LATEST_RUNTIME, - 1 /* numFailures */, 1 /* numSystemStops */, + 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); jsFlex = new JobStatus(jsFlex, nowElapsed + 30 * MINUTE_IN_MILLIS, NO_LATEST_RUNTIME, - 1 /* numFailures */, 1 /* numSystemStops */, + 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); @@ -847,21 +847,24 @@ public class FlexibilityControllerTest { JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("time", jb); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 0, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, + 0 /* numAbandonedFailures */, /* numSystemStops */ 0, 0, FROZEN_TIME, FROZEN_TIME); assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 1, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, + 0 /* numAbandonedFailures */, /* numSystemStops */ 1, 0, FROZEN_TIME, FROZEN_TIME); assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 10, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, + 0 /* numAbandonedFailures */, /* numSystemStops */ 10, 0, FROZEN_TIME, FROZEN_TIME); assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); @@ -1092,11 +1095,13 @@ public class FlexibilityControllerTest { JobInfo.Builder jb = createJob(0); JobStatus js = createJobStatus("time", jb); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, /* numSystemStops */ 0, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, + /* numAbandonedFailures */ 0, /* numSystemStops */ 0, 0, FROZEN_TIME, FROZEN_TIME); assertFalse(js.hasFlexibilityConstraint()); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 1, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, + /* numAbandonedFailures */ 0, /* numSystemStops */ 1, 0, FROZEN_TIME, FROZEN_TIME); assertFalse(js.hasFlexibilityConstraint()); } 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 2d0f4b69e2fe..86101cf591e7 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 @@ -459,35 +459,35 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // 2+ failures, priority should be lowered as much as possible. numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); numFailures = 8; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // Less than 2 failures, but job is downgraded. numFailures = 1; numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); } @@ -505,44 +505,44 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); // Failures in [2,4), priority should be lowered slightly. numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority()); numFailures = 3; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority()); // Failures in [4,6), priority should be lowered more. numFailures = 4; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); // 6+ failures, priority should be lowered as much as possible. numFailures = 6; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); numFailures = 12; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); } @@ -563,32 +563,32 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 4; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); // 6+ failures, priority should be lowered as much as possible. numFailures = 6; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); numFailures = 12; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); } @@ -606,28 +606,28 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // 2+ failures, priority shouldn't be affected while job is still a UI job numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); numFailures = 8; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // Job can no long run as user-initiated. Downgrades should be effective. @@ -641,28 +641,28 @@ public class JobStatusTest { numFailures = 1; numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); // 2+ failures, priority should start getting lower numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 8; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); } @@ -772,14 +772,14 @@ public class JobStatusTest { assertTrue(job.shouldTreatAsUserInitiatedJob()); JobStatus rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); assertFalse(job.shouldTreatAsUserInitiatedJob()); rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); rescheduledJob.removeInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); @@ -797,14 +797,14 @@ public class JobStatusTest { assertTrue(job.shouldTreatAsUserInitiatedJob()); JobStatus rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); assertFalse(job.shouldTreatAsUserInitiatedJob()); rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); rescheduledJob.removeInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/geometry/S2CellIdUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/location/geometry/S2CellIdUtilsTest.java new file mode 100644 index 000000000000..9e43b8122cd3 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/geometry/S2CellIdUtilsTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.location.geometry; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.location.geometry.S2CellIdUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class S2CellIdUtilsTest { + + // S2 cell ID of a level-30 cell in Times Square. + private static final long TIMES_SQUARE_S2_ID = + S2CellIdUtils.fromLatLngDegrees(40.758896, -73.985130); + + // Position of the Eiffel tower (outside of any parent cell from Times Square). + private static final double[] EIFFEL_TOWER_LATLNG = {48.858093, 2.294694}; + + // Test vector around TIMES_SQUARE_S2_ID: Cell IDs and the centers for levels 0 to 30. + // This test vector has been computed using the public S2 library in + // external/s2-geometry-library-java + private static final CellAndCenter[] TIMES_SQUARE_CELLS = { + new CellAndCenter("9", -0.0, -90.0), + new CellAndCenter("8c", 21.037511025421814, -67.38013505195958), + new CellAndCenter("89", 34.04786296943431, -79.38034472384487), + new CellAndCenter("89c", 38.79459515585768, -73.46516214265485), + new CellAndCenter("89d", 41.74704688465104, -76.45630866778862), + new CellAndCenter("89c4", 40.29416073145462, -74.96763653470293), + new CellAndCenter("89c3", 40.827706513259564, -74.21793256064282), + new CellAndCenter("89c24", 40.45771021423038, -73.84190634077625), + new CellAndCenter("89c25", 40.64307662867646, -74.03001224983848), + new CellAndCenter("89c25c", 40.708880489804564, -73.93598211433742), + new CellAndCenter("89c259", 40.75509755935301, -73.9830029344863), + new CellAndCenter("89c2584", 40.7781887758716, -74.00650903621303), + new CellAndCenter("89c2585", 40.766644611813284, -73.99475634561863), + new CellAndCenter("89c25854", 40.76087144655763, -73.98887973002674), + new CellAndCenter("89c25855", 40.75798459318946, -73.98594135473846), + new CellAndCenter("89c25855c", 40.75901097797799, -73.98447215023141), + new CellAndCenter("89c25855b", 40.758497791893824, -73.98520675388987), + new CellAndCenter("89c25855bc", 40.75875438651343, -73.98483945241185), + new CellAndCenter("89c25855b9", 40.758934819692875, -73.98502310323867), + new CellAndCenter("89c25855b9c", 40.75887067137071, -73.98511492858621), + new CellAndCenter("89c25855b9d", 40.75891577956465, -73.98516084124353), + new CellAndCenter("89c25855b9c4", 40.758893225473194, -73.98513788491626), + new CellAndCenter("89c25855b9c7", 40.75890124402366, -73.98512640675159), + new CellAndCenter("89c25855b9c6c", 40.758897234748815, -73.985132145834), + new CellAndCenter("89c25855b9c6d", 40.75889441548664, -73.98512927629281), + new CellAndCenter("89c25855b9c6c4", 40.75889582511775, -73.98513071106342), + new CellAndCenter("89c25855b9c6c3", 40.758896326277146, -73.98512999367811), + new CellAndCenter("89c25855b9c6c3c", 40.75889607569745, -73.98513035237076), + new CellAndCenter("89c25855b9c6c39", 40.75889589949357, -73.98513017302443), + new CellAndCenter("89c25855b9c6c39c", 40.75889596213849, -73.98513008335128), + new CellAndCenter("89c25855b9c6c39f", 40.75889599346095, -73.9851300385147)}; + + @Test + public void toLatLngDegrees_matchesTestVector() { + for (int level = 0; level <= 30; level++) { + double[] expected = TIMES_SQUARE_CELLS[level].mCenter; + long cellId = S2CellIdUtils.getParent(TIMES_SQUARE_S2_ID, level); + + double[] centerPoint = {0.0, 0.0}; + S2CellIdUtils.toLatLngDegrees(cellId, centerPoint); + + assertThat(approxEquals(centerPoint[0], expected[0])).isTrue(); + assertThat(approxEquals(centerPoint[1], expected[1])).isTrue(); + } + } + + private static boolean approxEquals(double a, double b) { + return Math.abs(a - b) <= 1e-14; + } + + @Test + public void containsLatLngDegrees_eachCellContainsItsCenter_works() { + for (int level = 0; level <= 30; level++) { + long cellId = TIMES_SQUARE_CELLS[level].toCellId(); + double[] center = TIMES_SQUARE_CELLS[level].mCenter; + + boolean isContained = S2CellIdUtils.containsLatLngDegrees(cellId, center[0], center[1]); + + assertThat(isContained).isTrue(); + } + } + + @Test + public void containsLatLngDegrees_testWithOutsidePoint() { + for (int level = 0; level <= 30; level++) { + long cellId = TIMES_SQUARE_CELLS[level].toCellId(); + + assertThat(S2CellIdUtils.containsLatLngDegrees(cellId, EIFFEL_TOWER_LATLNG[0], + EIFFEL_TOWER_LATLNG[1])).isFalse(); + } + } + + // A tuple with a S2 cell id, and a S2LatLng representing its center. + private static class CellAndCenter { + public String mToken; + public double[] mCenter; + + CellAndCenter(String token, double latDegrees, double lngDegrees) { + this.mToken = token; + this.mCenter = new double[] {latDegrees, lngDegrees}; + } + + // Converts from hex representation to long format. + long toCellId() { + long value = 0; + for (int pos = 0; pos < mToken.length(); pos++) { + int digitValue = Character.digit(mToken.charAt(pos), 16); + if (digitValue == -1) { + return -1; + } + value = value * 16 + digitValue; + } + value = value << (4 * (16 - mToken.length())); // remove implicit zeros + return value; + } + } +} diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java index e631cb66eaf7..313c01d5ce58 100644 --- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -18,7 +18,6 @@ package com.android.server.power.hint; import static com.android.server.power.hint.HintManagerService.CLEAN_UP_UID_DELAY_MILLIS; -import static com.android.server.power.hint.HintManagerService.DEFAULT_HEADROOM_PID; import static com.google.common.truth.Truth.assertThat; @@ -53,7 +52,9 @@ import android.hardware.common.fmq.MQDescriptor; import android.hardware.power.ChannelConfig; import android.hardware.power.ChannelMessage; import android.hardware.power.CpuHeadroomParams; +import android.hardware.power.CpuHeadroomResult; import android.hardware.power.GpuHeadroomParams; +import android.hardware.power.GpuHeadroomResult; import android.hardware.power.IPower; import android.hardware.power.SessionConfig; import android.hardware.power.SessionTag; @@ -1251,67 +1252,98 @@ public class HintManagerServiceTest { CpuHeadroomParams halParams1 = new CpuHeadroomParams(); halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN; halParams1.selectionType = CpuHeadroomParams.SelectionType.ALL; - halParams1.pid = Process.myPid(); + halParams1.tids = new int[]{Process.myPid()}; CpuHeadroomParamsInternal params2 = new CpuHeadroomParamsInternal(); params2.usesDeviceHeadroom = true; - params2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + params2.calculationType = CpuHeadroomParams.CalculationType.MIN; params2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE; CpuHeadroomParams halParams2 = new CpuHeadroomParams(); - halParams2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + halParams2.calculationType = CpuHeadroomParams.CalculationType.MIN; halParams2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE; - halParams2.pid = DEFAULT_HEADROOM_PID; + halParams2.tids = new int[]{}; + + CpuHeadroomParamsInternal params3 = new CpuHeadroomParamsInternal(); + params3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + params3.selectionType = CpuHeadroomParams.SelectionType.ALL; + CpuHeadroomParams halParams3 = new CpuHeadroomParams(); + halParams3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + halParams3.selectionType = CpuHeadroomParams.SelectionType.ALL; + halParams3.tids = new int[]{Process.myPid()}; + + // this params should not be cached as the window is not default + CpuHeadroomParamsInternal params4 = new CpuHeadroomParamsInternal(); + params4.calculationWindowMillis = 123; + CpuHeadroomParams halParams4 = new CpuHeadroomParams(); + halParams4.calculationType = CpuHeadroomParams.CalculationType.MIN; + halParams4.selectionType = CpuHeadroomParams.SelectionType.ALL; + halParams4.calculationWindowMillis = 123; + halParams4.tids = new int[]{Process.myPid()}; - float[] headroom1 = new float[] {0.1f}; - when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(headroom1); - float[] headroom2 = new float[] {0.1f, 0.5f}; - when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(headroom2); + float headroom1 = 0.1f; + CpuHeadroomResult halRet1 = CpuHeadroomResult.globalHeadroom(headroom1); + when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(halRet1); + float[] headroom2 = new float[] {0.2f, 0.2f}; + CpuHeadroomResult halRet2 = CpuHeadroomResult.perCoreHeadroom(headroom2); + when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(halRet2); + float headroom3 = 0.3f; + CpuHeadroomResult halRet3 = CpuHeadroomResult.globalHeadroom(headroom3); + when(mIPowerMock.getCpuHeadroom(eq(halParams3))).thenReturn(halRet3); + float headroom4 = 0.4f; + CpuHeadroomResult halRet4 = CpuHeadroomResult.globalHeadroom(headroom4); + when(mIPowerMock.getCpuHeadroom(eq(halParams4))).thenReturn(halRet4); HintManagerService service = createService(); clearInvocations(mIPowerMock); service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis(); verify(mIPowerMock, times(0)).getCpuHeadroomMinIntervalMillis(); - service.getBinderServiceInstance().getCpuHeadroom(params1); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); - service.getBinderServiceInstance().getCpuHeadroom(params2); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // verify cache is working clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getCpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(1)).getCpuHeadroom(any()); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 1 more second it should be served with cache still Thread.sleep(1000); clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getCpuHeadroom(any()); - - // after 1.5 more second it should be served with cache still as timer reset - Thread.sleep(1500); - clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getCpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(1)).getCpuHeadroom(any()); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval - Thread.sleep(2100); + Thread.sleep(1100); clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(4)).getCpuHeadroom(any()); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); } @Test @@ -1322,59 +1354,52 @@ public class HintManagerServiceTest { halParams1.calculationType = GpuHeadroomParams.CalculationType.MIN; GpuHeadroomParamsInternal params2 = new GpuHeadroomParamsInternal(); + params2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE; + params2.calculationWindowMillis = 123; GpuHeadroomParams halParams2 = new GpuHeadroomParams(); - params2.calculationType = - halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE; + halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE; + halParams2.calculationWindowMillis = 123; float headroom1 = 0.1f; - when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(headroom1); + GpuHeadroomResult halRet1 = GpuHeadroomResult.globalHeadroom(headroom1); + when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(halRet1); float headroom2 = 0.2f; - when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(headroom2); + GpuHeadroomResult halRet2 = GpuHeadroomResult.globalHeadroom(headroom2); + when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(halRet2); HintManagerService service = createService(); clearInvocations(mIPowerMock); service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis(); verify(mIPowerMock, times(0)).getGpuHeadroomMinIntervalMillis(); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(2)).getGpuHeadroom(any()); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // verify cache is working clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getGpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(1)).getGpuHeadroom(any()); + verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 1 more second it should be served with cache still Thread.sleep(1000); clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getGpuHeadroom(any()); - - // after 1.5 more second it should be served with cache still as timer reset - Thread.sleep(1500); - clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getGpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(1)).getGpuHeadroom(any()); + verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval - Thread.sleep(2100); + Thread.sleep(1100); clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(2)).getGpuHeadroom(any()); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java index c0be8652f303..4b91d8418fc3 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java @@ -79,8 +79,6 @@ public class AmbientDisplayPowerCalculatorTest { // 100,000,00 uC / 1000 (micro-/milli-) / 360 (seconds/hour) = 27.777778 mAh assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(27.777778); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -140,8 +138,6 @@ public class AmbientDisplayPowerCalculatorTest { // (seconds/hour) = 27.777778 mAh assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(83.33333); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -163,8 +159,6 @@ public class AmbientDisplayPowerCalculatorTest { .isEqualTo(90 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(15.0); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -195,7 +189,5 @@ public class AmbientDisplayPowerCalculatorTest { .isEqualTo(120 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(35.0); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java index 5d50e6c9f715..9da89fcf2e84 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java @@ -311,10 +311,6 @@ public class BatteryUsageStatsAtomTest { bus.getAggregateBatteryConsumer(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE), proto.deviceBatteryConsumer); - for (int i = 0; i < BatteryConsumer.POWER_COMPONENT_COUNT; i++) { - assertPowerComponentModel(i, abc.getPowerModel(i), proto); - } - // Now for the UidBatteryConsumers. final List<android.os.UidBatteryConsumer> uidConsumers = bus.getUidBatteryConsumers(); uidConsumers.sort((a, b) -> a.getUid() - b.getUid()); @@ -450,34 +446,6 @@ public class BatteryUsageStatsAtomTest { } } - /** - * Validates the PowerComponentModel object that matches powerComponent. - */ - private void assertPowerComponentModel(int powerComponent, - @BatteryConsumer.PowerModel int powerModel, BatteryUsageStatsAtomsProto proto) { - boolean found = false; - for (BatteryUsageStatsAtomsProto.PowerComponentModel powerComponentModel : - proto.componentModels) { - if (powerComponentModel.component == powerComponent) { - if (found) { - fail("Power component " + BatteryConsumer.powerComponentIdToString( - powerComponent) + " found multiple times in the proto"); - } - found = true; - final int expectedPowerModel = BatteryConsumer.powerModelToProtoEnum(powerModel); - assertEquals(expectedPowerModel, powerComponentModel.powerModel); - } - } - if (!found) { - final int model = BatteryConsumer.powerModelToProtoEnum(powerModel); - assertEquals( - "Power component " + BatteryConsumer.powerComponentIdToString(powerComponent) - + " was not found in the proto but has a defined power model.", - BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED, - model); - } - } - /** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */ private long convertMahToDc(double powerMah) { return (long) (powerMah * 36 + 0.5); @@ -486,7 +454,6 @@ public class BatteryUsageStatsAtomTest { private BatteryUsageStats buildBatteryUsageStats() { final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"}, - /* includePowerModels */ true, /* includeProcessStats */ true, /* includeScreenStateData */ false, /* includePowerStateData */ false, @@ -524,13 +491,13 @@ public class BatteryUsageStatsAtomTest { final BatteryConsumer.Key keyCached = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU, BatteryConsumer.PROCESS_STATE_CACHED); - uidBuilder.addConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE) + uidBuilder.addConsumedPower(keyFg, 9100) .addUsageDurationMillis(keyFg, 8100) - .addConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + .addConsumedPower(keyBg, (double) 9200) .addUsageDurationMillis(keyBg, 8200) - .addConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + .addConsumedPower(keyFgs, (double) 9300) .addUsageDurationMillis(keyFgs, 8300) - .addConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + .addConsumedPower(keyCached, (double) 9400) .addUsageDurationMillis(keyCached, 8400); final BatteryConsumer.Key keyCustomFg = uidBuilder.getKey( @@ -539,10 +506,8 @@ public class BatteryUsageStatsAtomTest { final BatteryConsumer.Key keyCustomBg = uidBuilder.getKey( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, BatteryConsumer.PROCESS_STATE_BACKGROUND); - uidBuilder.addConsumedPower( - keyCustomFg, 100, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); - uidBuilder.addConsumedPower( - keyCustomBg, 350, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + uidBuilder.addConsumedPower(keyCustomFg, 100); + uidBuilder.addConsumedPower(keyCustomBg, 350); builder.getOrCreateUidBatteryConsumerBuilder(UID_1) .setPackageWithHighestDrain("myPackage1") @@ -557,14 +522,11 @@ public class BatteryUsageStatsAtomTest { builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) .addConsumedPower(30000) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 20100, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + BatteryConsumer.POWER_COMPONENT_CPU, 20100) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_AUDIO, 0, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty + BatteryConsumer.POWER_COMPONENT_AUDIO, 0) // Empty .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CAMERA, 20150, - BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + BatteryConsumer.POWER_COMPONENT_CAMERA, 20150) .addConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200) .addUsageDurationMillis( @@ -576,8 +538,7 @@ public class BatteryUsageStatsAtomTest { builder.getAggregateBatteryConsumerBuilder( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 10100, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + BatteryConsumer.POWER_COMPONENT_CPU, 10100) .addConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200); @@ -587,7 +548,7 @@ public class BatteryUsageStatsAtomTest { @Test public void testLargeAtomTruncated() throws Exception { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[0], true, false, false, false, 0); + new BatteryUsageStats.Builder(new String[0], false, false, false, 0); // If not truncated, this BatteryUsageStats object would generate a proto buffer // significantly larger than 50 Kb for (int i = 0; i < 3000; i++) { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index 383616eb59eb..a3c7ece386c7 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java @@ -423,8 +423,6 @@ public class BatteryUsageStatsRule implements TestRule { mBatteryUsageStats = null; } final String[] customPowerComponentNames = mBatteryStats.getCustomEnergyConsumerNames(); - final boolean includePowerModels = (query.getFlags() - & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final boolean includeProcessStateData = (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0; final boolean includeScreenStateData = (query.getFlags() @@ -433,7 +431,7 @@ public class BatteryUsageStatsRule implements TestRule { & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE) != 0; final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold(); BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - customPowerComponentNames, includePowerModels, includeProcessStateData, + customPowerComponentNames, includeProcessStateData, includeScreenStateData, includePowerStateData, minConsumedPowerThreshold); SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats(); for (int i = 0; i < uidStats.size(); i++) { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java index 9771da590e37..dd50431b598e 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java @@ -17,8 +17,6 @@ package com.android.server.power.stats; import static android.os.BatteryConsumer.POWER_COMPONENT_ANY; -import static android.os.BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION; -import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED; import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND; import static android.os.BatteryConsumer.PROCESS_STATE_CACHED; import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND; @@ -237,7 +235,7 @@ public class BatteryUsageStatsTest { final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build(); final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[]{"FOO"}, true).build(); final BatteryUsageStats sum = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0) + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0) .add(stats1) .add(stats2) .build(); @@ -248,15 +246,13 @@ public class BatteryUsageStatsTest { for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { if (uidBatteryConsumer.getUid() == APP_UID1) { assertUidBatteryConsumer(uidBatteryConsumer, 1200 + 924, null, - 5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400 + 345, - POWER_MODEL_UNDEFINED, + 5321, 6900, 532, 423, 400 + 345, 500 + 456, 1167, 1478, true, 3554, 4732, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982, 444, 1110); } else if (uidBatteryConsumer.getUid() == APP_UID2) { assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar", - 1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, + 1111, 2220, 2, 333, 444, 555, 666, 777, true, 1777, 2443, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991, 321, 654); @@ -280,7 +276,7 @@ public class BatteryUsageStatsTest { @Test public void testAdd_customComponentMismatch() throws Exception { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0); + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0); final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"BAR"}, false).build(); assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); @@ -291,7 +287,7 @@ public class BatteryUsageStatsTest { @Test public void testAdd_processStateDataMismatch() throws Exception { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0); + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0); final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"FOO"}, false).build(); assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); @@ -328,7 +324,7 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, includeScreenState, includePowerState, 0) .setBatteryCapacity(4000) .setDischargePercentage(20) @@ -339,8 +335,8 @@ public class BatteryUsageStatsTest { addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo", 1000, 1500, 500, - 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, 500, 600, 800, + 300, 400, + 500, 600, 800, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456); addAggregateBatteryConsumer(builder, @@ -373,7 +369,7 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(customPowerComponentNames, true, + new BatteryUsageStats.Builder(customPowerComponentNames, includeProcessStateData, true, true, 0); builder.setDischargePercentage(30) .setDischargedPowerRange(1234, 2345) @@ -382,14 +378,14 @@ public class BatteryUsageStatsTest { addUidBatteryConsumer(builder, batteryStats, APP_UID1, null, 4321, 5400, 32, - 123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_ENERGY_CONSUMPTION, + 123, 345, 456, 567, 678, 1777, 7771, 1888, 8881, 1999, 9991, 321, 654); addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar", 1111, 2220, 2, - 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, 555, 666, 777, + 333, 444, + 555, 666, 777, 1777, 7771, 1888, 8881, 1999, 9991, 321, 654); addAggregateBatteryConsumer(builder, @@ -409,7 +405,7 @@ public class BatteryUsageStatsTest { MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain, int timeInProcessStateForeground, int timeInProcessStateBackground, int timeInProcessStateForegroundService, double screenPower, - int screenPowerModel, double cpuPower, int cpuPowerModel, double customComponentPower, + double cpuPower, double customComponentPower, int cpuDuration, int customComponentDuration, double cpuPowerForeground, int cpuDurationForeground, double cpuPowerBackground, int cpuDurationBackground, double cpuPowerFgs, int cpuDurationFgs, double cpuPowerCached, long cpuDurationCached) { @@ -423,9 +419,9 @@ public class BatteryUsageStatsTest { .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE, timeInProcessStateForegroundService) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel) + BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel) + BatteryConsumer.POWER_COMPONENT_CPU, cpuPower) .addConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower) .addUsageDurationMillis( @@ -460,21 +456,15 @@ public class BatteryUsageStatsTest { : uidBuilder.getKey( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, BatteryConsumer.PROCESS_STATE_BACKGROUND); - uidBuilder - .addConsumedPower(cpuFgKey, cpuPowerForeground, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + uidBuilder.addConsumedPower(cpuFgKey, cpuPowerForeground) .addUsageDurationMillis(cpuFgKey, cpuDurationForeground) - .addConsumedPower(cpuBgKey, cpuPowerBackground, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuBgKey, cpuPowerBackground) .addUsageDurationMillis(cpuBgKey, cpuDurationBackground) - .addConsumedPower(cpuFgsKey, cpuPowerFgs, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuFgsKey, cpuPowerFgs) .addUsageDurationMillis(cpuFgsKey, cpuDurationFgs) - .addConsumedPower(cachedKey, cpuPowerCached, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cachedKey, cpuPowerCached) .addUsageDurationMillis(cachedKey, cpuDurationCached) - .addConsumedPower(customBgKey, customComponentPower, - BatteryConsumer.POWER_MODEL_UNDEFINED) + .addConsumedPower(customBgKey, customComponentPower) .addUsageDurationMillis(customBgKey, customComponentDuration); } } @@ -518,18 +508,13 @@ public class BatteryUsageStatsTest { BatteryConsumer.PROCESS_STATE_UNSPECIFIED, BatteryConsumer.SCREEN_STATE_OTHER, BatteryConsumer.POWER_STATE_OTHER); - aggBuilder - .addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + aggBuilder.addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn) .addUsageDurationMillis(cpuBatScrOn, cpuDurationBatScrOn) - .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff) .addUsageDurationMillis(cpuBatScrOff, cpuDurationBatScrOff) - .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn) .addUsageDurationMillis(cpuChgScrOn, cpuDurationChgScrOn) - .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff) .addUsageDurationMillis(cpuChgScrOff, cpuDurationChgScrOff); } } @@ -544,8 +529,7 @@ public class BatteryUsageStatsTest { for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { if (uidBatteryConsumer.getUid() == APP_UID1) { assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo", - 1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, + 1000, 1500, 500, 300, 400, 500, 600, 800, true, 1777, 2388, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456); } else { @@ -596,8 +580,8 @@ public class BatteryUsageStatsTest { private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer, double consumedPower, String packageWithHighestDrain, int timeInProcessStateForeground, int timeInProcessStateBackground, int timeInProcessStateForegroundService, - int screenPower, int screenPowerModel, double cpuPower, - int cpuPowerModel, double customComponentPower, int cpuDuration, + int screenPower, double cpuPower, + double customComponentPower, int cpuDuration, int customComponentDuration, boolean processStateDataIncluded, double totalPowerForeground, double totalPowerBackground, double totalPowerFgs, double totalPowerCached, double cpuPowerForeground, int cpuDurationForeground, @@ -620,12 +604,8 @@ public class BatteryUsageStatsTest { PROCESS_STATE_FOREGROUND_SERVICE)).isEqualTo(timeInProcessStateForegroundService); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower); - assertThat(uidBatteryConsumer.getPowerModel( - BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPowerModel); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower); - assertThat(uidBatteryConsumer.getPowerModel( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower); assertThat(uidBatteryConsumer.getUsageDurationMillis( diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java index fe6424f91d83..c9cb0dfa98e4 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java @@ -82,16 +82,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.06944, 3000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.06944, 3000); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.19444, 9000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.19444, 9000); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26388, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26388, 12000); } @Test @@ -144,8 +144,6 @@ public class BluetoothPowerCalculatorTest { .isEqualTo(6166); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) .isWithin(PRECISION).of(0.1226666); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_BLUETOOTH, @@ -178,16 +176,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.08216, 3583); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.18169, 8416); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.30030, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.30030, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26386, 11999); } @Test @@ -202,16 +200,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.10378, 3583, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.10378, 3583); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.22950, 8416, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.22950, 8416); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.33333, 12000, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.33333, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.33329, 11999, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.33329, 11999); } @Test @@ -264,8 +262,6 @@ public class BluetoothPowerCalculatorTest { .isEqualTo(6166); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) .isWithin(PRECISION).of(0.8220561); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_BLUETOOTH, @@ -299,16 +295,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.08216, 3583); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.18169, 8416); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26388, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26386, 11999); } private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) { @@ -326,14 +322,12 @@ public class BluetoothPowerCalculatorTest { } private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer, - double powerMah, int durationMs, @BatteryConsumer.PowerModel int powerModel) { + double powerMah, int durationMs) { assertThat(batteryConsumer).isNotNull(); double consumedPower = batteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_BLUETOOTH); assertThat(consumedPower).isWithin(PRECISION).of(powerMah); - assertThat(batteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) - .isEqualTo(powerModel); long usageDurationMillis = batteryConsumer.getUsageDurationMillis( BatteryConsumer.POWER_COMPONENT_BLUETOOTH); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java index 7225f2d3995c..4cd3857a80bb 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java @@ -70,16 +70,12 @@ public class CameraPowerCalculatorTest { .isEqualTo(1000); assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.1); - assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID); assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(2000); assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.2); - assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer @@ -87,8 +83,6 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.3); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer @@ -96,8 +90,6 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.3); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -122,16 +114,12 @@ public class CameraPowerCalculatorTest { .isEqualTo(1000); assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.2); - assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID); assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(2000); assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.3); - assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer @@ -139,8 +127,6 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.5); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer @@ -148,7 +134,5 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.5); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java index 4cea72835fba..527db67a84ae 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java @@ -192,8 +192,6 @@ public class CpuPowerCalculatorTest { .isEqualTo(3333); assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(1.031677); - assertThat(uidConsumer1.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); @@ -201,21 +199,15 @@ public class CpuPowerCalculatorTest { .isEqualTo(7777); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(2.489544); - assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull(); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(3.52122); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(3.52122); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -264,8 +256,6 @@ public class CpuPowerCalculatorTest { .isEqualTo(3333); assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(3.18877); - assertThat(uidConsumer1.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); @@ -273,21 +263,15 @@ public class CpuPowerCalculatorTest { .isEqualTo(7777); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(7.44072); - assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull(); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(10.62949); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(10.62949); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java index 3b5658c2e40a..506bab4b3600 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java @@ -68,20 +68,14 @@ public class GnssPowerCalculatorTest { .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(0.1); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(0.1); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(0.1); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -107,27 +101,19 @@ public class GnssPowerCalculatorTest { .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(2.77777); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer consumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS)) .isEqualTo(2000); assertThat(consumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(5.55555); - assertThat(consumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(8.333333); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(8.333333); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java index 9b810bc01b4d..eba820e95783 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java @@ -164,8 +164,6 @@ public class MobileRadioPowerCalculatorTest { // = 4604000 mA-ms or 1.27888 mA-h assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.27888); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) @@ -178,22 +176,16 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.94); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 3/4 of total packets were sent by APP_UID so 75% of total UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.705); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.235); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -321,8 +313,6 @@ public class MobileRadioPowerCalculatorTest { // = 5177753 mA-ms or 1.43826 mA-h total consumption assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.43826); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) @@ -335,22 +325,16 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.09938); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 3/4 of total packets were sent by APP_UID so 75% of total UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.82453); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.27484); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -441,8 +425,6 @@ public class MobileRadioPowerCalculatorTest { // = 4604000 mA-ms or 1.27888 mA-h assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.27888); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) @@ -455,22 +437,16 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.94); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 3/4 of total packets were sent by APP_UID so 75% of total UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.705); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.235); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -648,24 +624,16 @@ public class MobileRadioPowerCalculatorTest { // 200ms phone on duration / 2000 total duration * 2.77778 mAh = 0.27777 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(2.5); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) .isWithin(PRECISION).of(0.27778); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.38541); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.38541); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -782,12 +750,8 @@ public class MobileRadioPowerCalculatorTest { // 1000ms phone on duration / 10000 total duration * 2.77778 mAh = 0.27777 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(2.5); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) .isWithin(PRECISION).of(0.27778); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); // CDMA2000 [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration // [720, 1080, 1440, 1800, 2160, 1440] mA . [10, 11, 12, 13, 14, 15] ms = 111600 mA-ms @@ -817,8 +781,6 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.91094); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); // 240 ms Rx Time, 1110 ms Tx Time, 1350 ms active time // 150 App 1 Rx Packets, 10 App 1 Tx packets @@ -841,15 +803,11 @@ public class MobileRadioPowerCalculatorTest { UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.27574); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.63520); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -936,12 +894,8 @@ public class MobileRadioPowerCalculatorTest { // 1000ms phone on duration / 10000 total duration * 2.77778 mAh = 0.27777 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(2.5); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) .isWithin(PRECISION).of(0.27778); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); // Estimated Rx/Tx modem consumption = 0.94 mAh @@ -949,14 +903,10 @@ public class MobileRadioPowerCalculatorTest { // 2.5 * 0.94 / 1.27888 = 1.83754 mAh assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.83754); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.83754); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java index 2da98e8b9a61..7f20035253f0 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java @@ -104,8 +104,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 200000000 + 5 / 45 * 300000000 mAs = 64.81 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(64.81481); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -116,8 +114,6 @@ public class ScreenPowerCalculatorTest { // Uid2 charge = 40 / 45 * 300000000 + 100000000 mAs = 101.85 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(101.85185); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -126,8 +122,6 @@ public class ScreenPowerCalculatorTest { // 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(166.66666); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -135,11 +129,8 @@ public class ScreenPowerCalculatorTest { assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(166.66666); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } - @Test public void testMeasuredEnergyBasedModel_multiDisplay() { mStatsRule.initMeasuredEnergyStatsLocked() @@ -202,8 +193,6 @@ public class ScreenPowerCalculatorTest { // (600000000 + 800000000) uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(388.88888); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -214,8 +203,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 20 / 80 * 600000000 mAs = 41.66666 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(41.66666); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -226,17 +213,12 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 60 / 80 * 600000000 + 800000000 mAs = 347.22222 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(347.22222); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(110 * MINUTE_IN_MS); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(388.88888); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); - } @Test @@ -277,8 +259,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 20 / 80 * 92.0 = 23.0 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(23.0); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -288,27 +268,20 @@ public class ScreenPowerCalculatorTest { // Uid2 charge = 60 / 80 * 92.0 = 69.0 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(69.0); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(92); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(92); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } - @Test public void testPowerProfileBasedModel_multiDisplay() { mStatsRule.setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 1, 60.0) @@ -364,8 +337,6 @@ public class ScreenPowerCalculatorTest { // 92 + 60 * 0.5 + 10 * 0.1 + 90 * 0.2 + 30 * 0.2 = 147 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(147); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -375,8 +346,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 20 / 110 * 147.0 = 23.0 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(26.72727); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -386,17 +355,12 @@ public class ScreenPowerCalculatorTest { // Uid2 charge = 90 / 110 * 92.0 = 69.0 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(120.272727); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(110 * MINUTE_IN_MS); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(147); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); - } private void setProcState(int uid, int procState, boolean resumed, long realtimeMs, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java index 8e221be261e9..827d2f8f04c8 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java @@ -159,22 +159,16 @@ public class WifiPowerCalculatorTest { .isEqualTo(2473); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.3964); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(4001); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.86666); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.866666); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -214,8 +208,6 @@ public class WifiPowerCalculatorTest { .isEqualTo(12423); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(2.0214666); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_WIFI, @@ -248,22 +240,16 @@ public class WifiPowerCalculatorTest { /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(4002); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.27777); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.277777); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -302,8 +288,6 @@ public class WifiPowerCalculatorTest { .isEqualTo(12423); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(1.0325211); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_WIFI, @@ -349,8 +333,6 @@ public class WifiPowerCalculatorTest { .isEqualTo(1000); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.8231573); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -371,8 +353,6 @@ public class WifiPowerCalculatorTest { /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } private WifiActivityEnergyInfo buildWifiActivityEnergyInfo(long timeSinceBoot, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java index f7a16383974d..cca60339acf7 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java @@ -176,7 +176,6 @@ public class BasePowerStatsProcessorTest { powerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0); BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], - /* includePowerModels */ false, /* includeProcessStateData */ true, /* includeScreenStateData */ true, /* includesPowerStateData */ true, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java index 4643dddb1a33..38fe6134d992 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java @@ -342,7 +342,7 @@ public class PowerStatsExporterTest { PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0); - BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], false, + BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], includeProcessStateData, includeScreenStateData, includesPowerStateData, 0); exporter.populateBatteryUsageStatsBuilder(builder, aps); return builder.build(); @@ -361,7 +361,7 @@ public class PowerStatsExporterTest { private void breakdownByProcState_fullRange(boolean includeScreenStateData, boolean includePowerStateData) throws Exception { BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - new String[]{"cu570m"}, /* includePowerModels */ false, + new String[]{"cu570m"}, /* includeProcessStateData */ true, includeScreenStateData, includePowerStateData, /* powerThreshold */ 0); exportAggregatedPowerStats(builder, 1000, 10000); @@ -406,7 +406,7 @@ public class PowerStatsExporterTest { @Test public void breakdownByProcState_subRange() throws Exception { BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - new String[]{"cu570m"}, /* includePowerModels */ false, + new String[]{"cu570m"}, /* includeProcessStateData */ true, true, true, /* powerThreshold */ 0); exportAggregatedPowerStats(builder, 3700, 6700); @@ -438,7 +438,7 @@ public class PowerStatsExporterTest { @Test public void combinedProcessStates() throws Exception { BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - new String[]{"cu570m"}, /* includePowerModels */ false, + new String[]{"cu570m"}, /* includeProcessStateData */ false, true, true, /* powerThreshold */ 0); exportAggregatedPowerStats(builder, 1000, 10000); diff --git a/services/tests/security/intrusiondetection/Android.bp b/services/tests/security/intrusiondetection/Android.bp index 00ac90807dff..8d674b14feac 100644 --- a/services/tests/security/intrusiondetection/Android.bp +++ b/services/tests/security/intrusiondetection/Android.bp @@ -19,15 +19,20 @@ android_test { "androidx.test.rules", "androidx.test.runner", "compatibility-device-util-axt", + "coretests-aidl", "frameworks-base-testutils", "junit", "platform-test-annotations", + "servicestests-utils", "services.core", "truth", "Nene", "Harrier", "TestApp", ], + data: [ + ":TestIntrusionDetectionApp", + ], platform_apis: true, diff --git a/services/tests/security/intrusiondetection/AndroidManifest.xml b/services/tests/security/intrusiondetection/AndroidManifest.xml index f388e7ea8590..b30710d9bcbe 100644 --- a/services/tests/security/intrusiondetection/AndroidManifest.xml +++ b/services/tests/security/intrusiondetection/AndroidManifest.xml @@ -18,12 +18,25 @@ package="com.android.server.security.intrusiondetection.tests"> <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" /> - <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> + <uses-permission android:name="android.permission.INTERNET"/> - <application android:testOnly="true"> + <application android:testOnly="true" android:debuggable="true" android:usesCleartextTraffic="true"> <uses-library android:name="android.test.runner"/> + <receiver android:name="com.android.server.security.intrusiondetection.IntrusionDetectionAdminReceiver" + android:permission="android.permission.BIND_DEVICE_ADMIN" + android:exported="true"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_admin"/> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> + </intent-filter> + </receiver> </application> + <queries> + <package android:name="com.android.coretests.apps.testapp" /> + </queries> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.server.security.intrusiondetection.tests" android:label="Frameworks IntrusionDetection Services Tests"/> diff --git a/services/tests/security/intrusiondetection/AndroidTest.xml b/services/tests/security/intrusiondetection/AndroidTest.xml index 42cb9e3236e0..6489dea4a508 100644 --- a/services/tests/security/intrusiondetection/AndroidTest.xml +++ b/services/tests/security/intrusiondetection/AndroidTest.xml @@ -20,6 +20,7 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true"/> <option name="test-file-name" value="IntrusionDetectionServiceTests.apk"/> + <option name="test-file-name" value="TestIntrusionDetectionApp.apk"/> <option name="install-arg" value="-t" /> </target_preparer> diff --git a/services/tests/security/intrusiondetection/res/xml/device_admin.xml b/services/tests/security/intrusiondetection/res/xml/device_admin.xml new file mode 100644 index 000000000000..f8cd8f0b9b44 --- /dev/null +++ b/services/tests/security/intrusiondetection/res/xml/device_admin.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<device-admin xmlns:android="http://schemas.android.com/apk/res/android"> +</device-admin> diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java index bc854cf6488b..e505ebeb2946 100644 --- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java +++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java @@ -16,14 +16,19 @@ package com.android.server.security.intrusiondetection; +import static android.Manifest.permission.INTERNET; import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE; import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -33,8 +38,14 @@ import static org.mockito.Mockito.verify; import android.annotation.SuppressLint; import android.app.admin.ConnectEvent; import android.app.admin.DnsEvent; +import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.Bundle; import android.os.Looper; import android.os.PermissionEnforcer; import android.os.RemoteException; @@ -43,19 +54,47 @@ import android.os.test.TestLooper; import android.security.intrusiondetection.IIntrusionDetectionServiceCommandCallback; import android.security.intrusiondetection.IIntrusionDetectionServiceStateCallback; import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.util.Log; import androidx.test.core.app.ApplicationProvider; +import com.android.bedstead.harrier.BedsteadJUnit4; +import com.android.bedstead.harrier.annotations.AfterClass; +import com.android.bedstead.harrier.annotations.BeforeClass; +import com.android.bedstead.multiuser.annotations.RequireRunOnSystemUser; +import com.android.bedstead.nene.TestApis; +import com.android.bedstead.nene.devicepolicy.DeviceOwner; +import com.android.bedstead.nene.exceptions.NeneException; +import com.android.bedstead.permissions.CommonPermissions; +import com.android.bedstead.permissions.PermissionContext; +import com.android.bedstead.permissions.annotations.EnsureHasPermission; +import com.android.coretests.apps.testapp.LocalIntrusionDetectionEventTransport; +import com.android.internal.infra.AndroidFuture; import com.android.server.ServiceThread; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; - +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +@RunWith(BedsteadJUnit4.class) public class IntrusionDetectionServiceTest { private static final int STATE_UNKNOWN = IIntrusionDetectionServiceStateCallback.State.UNKNOWN; @@ -73,20 +112,49 @@ public class IntrusionDetectionServiceTest { private static final int ERROR_DATA_SOURCE_UNAVAILABLE = IIntrusionDetectionServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; + private static DeviceOwner sDeviceOwner; + private Context mContext; private IntrusionDetectionEventTransportConnection mIntrusionDetectionEventTransportConnection; private DataAggregator mDataAggregator; private IntrusionDetectionService mIntrusionDetectionService; + private IBinder mService; private TestLooper mTestLooper; private Looper mLooper; private TestLooper mTestLooperOfDataAggregator; private Looper mLooperOfDataAggregator; private FakePermissionEnforcer mPermissionEnforcer; + private boolean mBoundToLoggingService = false; + private static final String TEST_PKG = + "com.android.coretests.apps.testapp"; + private static final String TEST_SERVICE = TEST_PKG + ".TestLoggingService"; + + @BeforeClass + public static void setDeviceOwner() { + ComponentName admin = + new ComponentName( + ApplicationProvider.getApplicationContext(), + IntrusionDetectionAdminReceiver.class); + try { + sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin); + } catch (NeneException e) { + fail("Failed to set device owner " + admin.flattenToString() + ": " + e); + } + } + + @AfterClass + public static void removeDeviceOwner() { + try { + sDeviceOwner.remove(); + } catch (NeneException e) { + fail("Failed to remove device owner : " + e); + } + } @SuppressLint("VisibleForTests") @Before - public void setUp() { - mContext = spy(ApplicationProvider.getApplicationContext()); + public void setUp() throws Exception { + mContext = ApplicationProvider.getApplicationContext(); mPermissionEnforcer = new FakePermissionEnforcer(); mPermissionEnforcer.grant(READ_INTRUSION_DETECTION_STATE); @@ -343,6 +411,234 @@ public class IntrusionDetectionServiceTest { assertNotNull(receivedEvents.get(2).getDnsEvent()); } + @Test + @RequireRunOnSystemUser + public void testDataSources_Initialize_HasDeviceOwner() throws Exception { + NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator); + SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator); + + assertTrue(networkLogSource.initialize()); + assertTrue(securityLogSource.initialize()); + } + + @Test + @RequireRunOnSystemUser + public void testDataSources_Initialize_NoDeviceOwner() throws Exception { + NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator); + SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator); + ComponentName admin = sDeviceOwner.componentName(); + + try { + sDeviceOwner.remove(); + assertFalse(networkLogSource.initialize()); + assertFalse(securityLogSource.initialize()); + } finally { + sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin); + } + } + + @Test + @RequireRunOnSystemUser + @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + public void testDataAggregator_AddSecurityEvent() throws Exception { + mIntrusionDetectionService.setState(STATE_ENABLED); + ServiceThread mockThread = spy(ServiceThread.class); + mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread); + assertTrue(mDataAggregator.initialize()); + + // SecurityLogging generates a number of events and callbacks, so create a latch to wait for + // the given event. + String eventString = this.getClass().getName() + ".testSecurityEvent"; + + final CountDownLatch latch = new CountDownLatch(1); + // TODO: Replace this mock when the IntrusionDetectionEventTransportConnection is ready. + doAnswer( + new Answer<Boolean>() { + @Override + public Boolean answer(InvocationOnMock input) { + List<IntrusionDetectionEvent> receivedEvents = + (List<IntrusionDetectionEvent>) input.getArguments()[0]; + for (IntrusionDetectionEvent event : receivedEvents) { + if (event.getType() == IntrusionDetectionEvent.SECURITY_EVENT) { + SecurityEvent securityEvent = event.getSecurityEvent(); + Object[] eventData = (Object[]) securityEvent.getData(); + if (securityEvent.getTag() == SecurityLog.TAG_KEY_GENERATED + && eventData[1].equals(eventString)) { + latch.countDown(); + } + } + } + return true; + } + }) + .when(mIntrusionDetectionEventTransportConnection).addData(any()); + mDataAggregator.enable(); + + // Generate the security event. + generateSecurityEvent(eventString); + TestApis.devicePolicy().forceSecurityLogs(); + + // Verify the event is received. + mTestLooper.startAutoDispatch(); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + mTestLooper.stopAutoDispatch(); + + mDataAggregator.disable(); + } + + @Test + @RequireRunOnSystemUser + @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + public void testDataAggregator_AddNetworkEvent() throws Exception { + mIntrusionDetectionService.setState(STATE_ENABLED); + ServiceThread mockThread = spy(ServiceThread.class); + mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread); + assertTrue(mDataAggregator.initialize()); + + // Network logging may log multiple and callbacks, so create a latch to wait for + // the given event. + // eventServer must be a valid domain to generate a network log event. + String eventServer = "google.com"; + final CountDownLatch latch = new CountDownLatch(1); + // TODO: Replace this mock when the IntrusionDetectionEventTransportConnection is ready. + doAnswer( + new Answer<Boolean>() { + @Override + public Boolean answer(InvocationOnMock input) { + List<IntrusionDetectionEvent> receivedEvents = + (List<IntrusionDetectionEvent>) input.getArguments()[0]; + for (IntrusionDetectionEvent event : receivedEvents) { + if (event.getType() + == IntrusionDetectionEvent.NETWORK_EVENT_DNS) { + DnsEvent dnsEvent = event.getDnsEvent(); + if (dnsEvent.getHostname().equals(eventServer)) { + latch.countDown(); + } + } + } + return true; + } + }) + .when(mIntrusionDetectionEventTransportConnection).addData(any()); + mDataAggregator.enable(); + + // Generate the network event. + generateNetworkEvent(eventServer); + TestApis.devicePolicy().forceNetworkLogs(); + + // Verify the event is received. + mTestLooper.startAutoDispatch(); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + mTestLooper.stopAutoDispatch(); + + mDataAggregator.disable(); + } + + /** Emits a given string into security log (if enabled). */ + private void generateSecurityEvent(String eventString) + throws IllegalArgumentException, GeneralSecurityException, IOException { + if (eventString == null || eventString.isEmpty()) { + throw new IllegalArgumentException( + "Error generating security event: eventString must not be empty"); + } + + final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); + keyGen.initialize( + new KeyGenParameterSpec.Builder(eventString, KeyProperties.PURPOSE_SIGN).build()); + // Emit key generation event. + final KeyPair keyPair = keyGen.generateKeyPair(); + assertNotNull(keyPair); + + final KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); + ks.load(null); + // Emit key destruction event. + ks.deleteEntry(eventString); + } + + /** Emits a given string into network log (if enabled). */ + private void generateNetworkEvent(String server) throws IllegalArgumentException, IOException { + if (server == null || server.isEmpty()) { + throw new IllegalArgumentException( + "Error generating network event: server must not be empty"); + } + + HttpURLConnection urlConnection = null; + int connectionTimeoutMS = 2_000; + try (PermissionContext p = TestApis.permissions().withPermission(INTERNET)) { + final URL url = new URL("http://" + server); + urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setConnectTimeout(connectionTimeoutMS); + urlConnection.setReadTimeout(connectionTimeoutMS); + urlConnection.getResponseCode(); + } finally { + if (urlConnection != null) { + urlConnection.disconnect(); + } + } + } + + @Test + public void test_StartIntrusionDetectionEventTransportService() { + final String TAG = "test_StartIntrusionDetectionEventTransportService"; + ServiceConnection serviceConnection = null; + + assertEquals(false, mBoundToLoggingService); + try { + serviceConnection = startTestService(); + assertEquals(true, mBoundToLoggingService); + assertNotNull(serviceConnection); + } catch (SecurityException e) { + Log.e(TAG, "SecurityException while starting: ", e); + fail("Exception thrown while connecting to service"); + } catch (InterruptedException e) { + Log.e(TAG, "InterruptedException while starting: ", e); + fail("Interrupted while connecting to service"); + } finally { + mContext.unbindService(serviceConnection); + } + } + + private ServiceConnection startTestService() throws SecurityException, InterruptedException { + final String TAG = "startTestService"; + final CountDownLatch latch = new CountDownLatch(1); + LocalIntrusionDetectionEventTransport transport = + new LocalIntrusionDetectionEventTransport(); + + ServiceConnection serviceConnection = new ServiceConnection() { + // Called when connection with the service is established. + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mService = transport.getBinder(); + mBoundToLoggingService = true; + latch.countDown(); + } + + // Called when the connection with the service disconnects unexpectedly. + @Override + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected"); + mBoundToLoggingService = false; + } + }; + + Intent intent = new Intent(); + intent.setComponent(new ComponentName(TEST_PKG, TEST_SERVICE)); + mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + latch.await(5, TimeUnit.SECONDS); + + // call the methods on the transport object + IntrusionDetectionEvent event = + new IntrusionDetectionEvent(new SecurityEvent(123, new byte[15])); + List<IntrusionDetectionEvent> events = new ArrayList<>(); + events.add(event); + assertTrue(transport.initialize()); + assertTrue(transport.addData(events)); + assertTrue(transport.release()); + assertEquals(1, transport.getEvents().size()); + + return serviceConnection; + } + private class MockInjector implements IntrusionDetectionService.Injector { private final Context mContext; diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/Android.bp b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/Android.bp new file mode 100644 index 000000000000..ca5952b140c1 --- /dev/null +++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_team: "trendy_team_platform_security", + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "TestIntrusionDetectionApp", + + static_libs: [ + "frameworks-base-testutils", + "services.core", + "servicestests-utils", + ], + + srcs: ["**/*.java"], + + platform_apis: true, + certificate: "platform", + dxflags: ["--multi-dex"], + optimize: { + enabled: false, + }, +} diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml new file mode 100644 index 000000000000..7cc75ab70571 --- /dev/null +++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.coretests.apps.testapp"> + + <application> + <service android:name=".TestLoggingService" + android:exported="true" /> + </application> +</manifest>
\ No newline at end of file diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java new file mode 100644 index 000000000000..f0012da44fa4 --- /dev/null +++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance + with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + */ + +package com.android.coretests.apps.testapp; + +import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.security.intrusiondetection.IntrusionDetectionEventTransport; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class that extends {@link IntrusionDetectionEventTransport} to provide a + * local transport mechanism for testing purposes. This implementation overrides + * the {@link #initialize()}, {@link #addData(List)}, and {@link #release()} methods + * to manage events locally within the test environment. + * + * For now, the implementation returns true for all methods since we don't + * have a real data source to send events to. + */ +public class LocalIntrusionDetectionEventTransport extends IntrusionDetectionEventTransport { + private List<IntrusionDetectionEvent> mEvents = new ArrayList<>(); + + @Override + public boolean initialize() { + return true; + } + + @Override + public boolean addData(List<IntrusionDetectionEvent> events) { + mEvents.addAll(events); + return true; + } + + @Override + public boolean release() { + return true; + } + + public List<IntrusionDetectionEvent> getEvents() { + return mEvents; + } +}
\ No newline at end of file diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java new file mode 100644 index 000000000000..e4bf987402fd --- /dev/null +++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.coretests.apps.testapp; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.Process; + +import com.android.internal.infra.AndroidFuture; + + +public class TestLoggingService extends Service { + private static final String TAG = "TestLoggingService"; + private LocalIntrusionDetectionEventTransport mLocalIntrusionDetectionEventTransport; + + public TestLoggingService() { + mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport(); + } + + // Binder given to clients. + @Override + public IBinder onBind(Intent intent) { + return mLocalIntrusionDetectionEventTransport.getBinder(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java index ae78dfe624c6..cc5be7ebba62 100644 --- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -40,6 +41,7 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.os.Bundle; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemProperties; @@ -50,6 +52,12 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.os.IBinaryTransparencyService; +import com.android.server.pm.BackgroundInstallControlService; +import com.android.server.pm.BackgroundInstallControlCallbackHelper; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.AndroidPackageSplit; +import com.android.server.pm.pkg.PackageStateInternal; import org.junit.After; import org.junit.Assert; @@ -68,6 +76,9 @@ import java.util.List; public class BinaryTransparencyServiceTest { private static final String TAG = "BinaryTransparencyServiceTest"; + private static final String TEST_PKG_NAME = "testPackageName"; + private static final long TEST_VERSION_CODE = 1L; + private Context mContext; private BinaryTransparencyService mBinaryTransparencyService; private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface; @@ -83,6 +94,8 @@ public class BinaryTransparencyServiceTest { private PackageManager mPackageManager; @Mock private PackageManagerInternal mPackageManagerInternal; + @Mock + private BinaryTransparencyService.BicCallbackHandler.IBicAppInfoHelper mBicAppInfoHelper; @Captor private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> @@ -91,6 +104,9 @@ public class BinaryTransparencyServiceTest { private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; + @Captor + private ArgumentCaptor<IBinaryTransparencyService.AppInfo> appInfoCaptor; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -262,4 +278,69 @@ public class BinaryTransparencyServiceTest { eq("") /* softwareVersion */ ); } + + @Test + public void BicCallbackHandler_uploads_mba_metrics() { + Bundle data = setupBicCallbackHandlerTest(false, + BinaryTransparencyService.MBA_STATUS_NEW_INSTALL); + + BinaryTransparencyService.BicCallbackHandler handler = + new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper); + handler.sendResult(data); + + verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture()); + Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName); + Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion); + } + + @Test + public void BicCallbackHandler_uploads_mba_metrics_for_preloads() { + Bundle data = setupBicCallbackHandlerTest(true, + BinaryTransparencyService.MBA_STATUS_UPDATED_PRELOAD); + + BinaryTransparencyService.BicCallbackHandler handler = + new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper); + handler.sendResult(data); + + verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture()); + Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName); + Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion); + } + + @Test + public void BicCallbackHandler_uploads_mba_metrics_for_uninstalls() { + Bundle data = new Bundle(); + data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY, + TEST_PKG_NAME); + data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY, + BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL); + + BinaryTransparencyService.BicCallbackHandler handler = + new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper); + handler.sendResult(data); + + verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture()); + Assert.assertEquals(TEST_PKG_NAME ,appInfoCaptor.getValue().packageName); + Assert.assertEquals(BinaryTransparencyService.MBA_STATUS_UNINSTALLED, + appInfoCaptor.getValue().mbaStatus); + } + + private Bundle setupBicCallbackHandlerTest(boolean isUpdatedSystemApp, + int expectedBtsMbaStatus) { + Bundle data = new Bundle(); + data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY, + TEST_PKG_NAME); + data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY, + BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL); + PackageStateInternal mockPackageState = mock(PackageStateInternal.class); + when(mPackageManagerInternal.getPackageStateInternal(TEST_PKG_NAME)) + .thenReturn(mockPackageState); + when(mockPackageState.isUpdatedSystemApp()).thenReturn(isUpdatedSystemApp); + IBinaryTransparencyService.AppInfo appInfo = new IBinaryTransparencyService.AppInfo(); + appInfo.packageName = TEST_PKG_NAME; + appInfo.longVersion = TEST_VERSION_CODE; + when(mBicAppInfoHelper.collectAppInfo(mockPackageState, expectedBtsMbaStatus)) + .thenReturn(List.of(appInfo)); + return data; + } } 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 492838e9b4fb..27de7644f6b2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -38,7 +38,6 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE; import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG; -import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; import static com.google.common.truth.Truth.assertThat; @@ -601,7 +600,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) public void testSetConnectionNull_borderFlagEnabled_unregisterFullScreenMagnification() throws RemoteException { mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 7e0c12a5a545..ac27a971102a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -21,7 +21,6 @@ import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MOD import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; -import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -308,7 +307,6 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) public void testSetScale_noConnection_doNothing() { register(TEST_DISPLAY); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index f0d3456a39de..c878799109dc 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -57,10 +57,6 @@ import android.hardware.display.DisplayManagerInternal; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; @@ -82,7 +78,6 @@ import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; -import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; @@ -101,9 +96,6 @@ import org.mockito.stubbing.Answer; @RunWith(AndroidJUnit4.class) public class MagnificationControllerTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; private static final int TEST_SERVICE_ID = 1; private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION = @@ -1365,8 +1357,7 @@ public class MagnificationControllerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - public void onFullscreenMagnificationActivationState_systemUiBorderFlagOn_notifyConnection() { + public void onFullscreenMagnificationActivationState_notifyConnection() { mMagnificationController.onFullScreenMagnificationActivationState( TEST_DISPLAY, /* activated= */ true); @@ -1374,17 +1365,6 @@ public class MagnificationControllerTest { .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); } - @Test - @RequiresFlagsDisabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - public void - onFullscreenMagnificationActivationState_systemUiBorderFlagOff_neverNotifyConnection() { - mMagnificationController.onFullScreenMagnificationActivationState( - TEST_DISPLAY, /* activated= */ true); - - verify(mMagnificationConnectionManager, never()) - .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); - } - private void setMagnificationEnabled(int mode) throws RemoteException { setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); } 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 a7bf3e194dd9..30aa8cebdff6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5036,8 +5036,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { @Test @RequiresFlagsEnabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED) public void testSetSecondaryLockscreenEnabled() throws Exception { - mContext.binder.callingUid = DpmMockContext.CALLER_UID; - verifySetSecondaryLockscreenEnabled(false); verifySetSecondaryLockscreenEnabled(true); } @@ -5045,6 +5043,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void verifySetSecondaryLockscreenEnabled(boolean enabled) throws Exception { reset(getServices().supervisionManagerInternal); + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + doReturn(DpmMockContext.CALLER_UID).when(getServices().packageManagerInternal) + .getPackageUid(any(), anyLong(), anyInt()); + dpm.setSecondaryLockscreenEnabled(admin1, enabled); verify(getServices().supervisionManagerInternal).setSupervisionLockscreenEnabledForUser( CALLER_USER_HANDLE, enabled, null); diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index ab5a5a9bf2fe..5127b2d11e44 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -24,9 +24,14 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; import android.content.Context; +import android.frameworks.devicestate.DeviceStateConfiguration; +import android.frameworks.devicestate.ErrorCode; +import android.frameworks.devicestate.IDeviceStateListener; +import android.frameworks.devicestate.IDeviceStateService; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateInfo; import android.hardware.devicestate.DeviceStateRequest; @@ -34,6 +39,7 @@ import android.hardware.devicestate.IDeviceStateManagerCallback; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; @@ -336,6 +342,53 @@ public final class DeviceStateManagerServiceTest { } @Test + public void halRegisterUnregisterCallback() throws RemoteException { + IDeviceStateService halService = mService.getHalBinderService(); + IDeviceStateListener halListener = new IDeviceStateListener.Stub() { + @Override + public void onDeviceStateChanged(DeviceStateConfiguration deviceState) { } + + @Override + public int getInterfaceVersion() { + return IDeviceStateListener.VERSION; + } + + @Override + public String getInterfaceHash() { + return IDeviceStateListener.HASH; + } + }; + + int errorCode = ErrorCode.OK; + try { + halService.unregisterListener(halListener); + } catch(ServiceSpecificException e) { + errorCode = e.errorCode; + } + assertEquals(errorCode, ErrorCode.BAD_INPUT); + + errorCode = ErrorCode.OK; + try { + halService.unregisterListener(null); + } catch(ServiceSpecificException e) { + errorCode = e.errorCode; + } + assertEquals(errorCode, ErrorCode.BAD_INPUT); + + halService.registerListener(halListener); + + errorCode = ErrorCode.OK; + try { + halService.registerListener(halListener); + } catch (ServiceSpecificException e) { + errorCode = e.errorCode; + } + assertEquals(errorCode, ErrorCode.ALREADY_EXISTS); + + halService.unregisterListener(halListener); + } + + @Test public void registerCallback() throws RemoteException { final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java index c7574bdc3f6c..dd4101e9796f 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java @@ -41,11 +41,14 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; +import android.media.tv.flags.Flags; import android.os.Binder; import android.os.Looper; import android.os.RemoteException; import android.os.test.TestLooper; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.stats.hdmi.HdmiStatsEnums; import androidx.test.InstrumentationRegistry; @@ -54,6 +57,7 @@ import androidx.test.filters.SmallTest; import com.android.server.SystemService; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -83,6 +87,9 @@ public class HdmiCecAtomLoggingTest { private HdmiEarcController mHdmiEarcController; private FakeEarcNativeWrapper mEarcNativeWrapper; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Before public void setUp() throws RemoteException { mHdmiCecAtomWriterSpy = spy(new HdmiCecAtomWriter()); @@ -232,7 +239,8 @@ public class HdmiCecAtomLoggingTest { HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN, HdmiStatsEnums.VOLUME_MUTE, HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN, - HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN); + HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN, + HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID); } @Test @@ -258,7 +266,8 @@ public class HdmiCecAtomLoggingTest { HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN, HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN, HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN, - HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN); + HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN, + HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID); } @Test @@ -285,7 +294,8 @@ public class HdmiCecAtomLoggingTest { HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN, HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN, Constants.MESSAGE_RECORD_ON, - HdmiStatsEnums.UNRECOGNIZED_OPCODE); + HdmiStatsEnums.UNRECOGNIZED_OPCODE, + HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID); } @Test @@ -311,7 +321,8 @@ public class HdmiCecAtomLoggingTest { HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN, HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN, HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN, - HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN); + HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN, + HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID); } @Test @@ -337,6 +348,59 @@ public class HdmiCecAtomLoggingTest { } @Test + @EnableFlags({Flags.FLAG_HDMI_CONTROL_COLLECT_PHYSICAL_ADDRESS}) + public void testMessageReported_writesAtom_reportPhysicalAddress() { + HdmiCecMessage message = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_PLAYBACK_1, 0x1234, HdmiDeviceInfo.DEVICE_PLAYBACK); + + mHdmiCecAtomWriterSpy.messageReported( + message, + HdmiStatsEnums.INCOMING, + 1234); + + verify(mHdmiCecAtomWriterSpy, times(1)) + .writeHdmiCecMessageReportedAtom( + 1234, + HdmiStatsEnums.INCOMING, + Constants.ADDR_PLAYBACK_1, + Constants.ADDR_BROADCAST, + Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, + HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN, + HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN, + HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN, + HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN, + 0x1234); + } + + @Test + @EnableFlags({Flags.FLAG_HDMI_CONTROL_COLLECT_PHYSICAL_ADDRESS}) + public void testMessageReported_writesAtom_reportPhysicalAddress_noParams() { + HdmiCecMessage message = HdmiCecMessage.build( + Constants.ADDR_PLAYBACK_1, + Constants.ADDR_BROADCAST, + Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, + new byte[0]); + + mHdmiCecAtomWriterSpy.messageReported( + message, + HdmiStatsEnums.INCOMING, + 1234); + + verify(mHdmiCecAtomWriterSpy, times(1)) + .writeHdmiCecMessageReportedAtom( + 1234, + HdmiStatsEnums.INCOMING, + Constants.ADDR_PLAYBACK_1, + Constants.ADDR_BROADCAST, + Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, + HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN, + HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN, + HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN, + HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN, + HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID); + } + + @Test public void testDsmStatusChanged_onWakeUp_ArcSupported_writesAtom_logReasonWake() { mHdmiControlServiceSpy.setSoundbarMode(HdmiControlManager.SOUNDBAR_MODE_DISABLED); Mockito.clearInvocations(mHdmiCecAtomWriterSpy); diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml index 787f4e85c012..3fc7c7692abc 100644 --- a/services/tests/wmtests/res/xml/bookmarks.xml +++ b/services/tests/wmtests/res/xml/bookmarks.xml @@ -22,7 +22,7 @@ androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_CONTACTS" - androidprv:keycode="KEYCODE_C" + androidprv:keycode="KEYCODE_P" androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_EMAIL" @@ -30,21 +30,13 @@ androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_CALENDAR" - androidprv:keycode="KEYCODE_K" + androidprv:keycode="KEYCODE_C" androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_MAPS" androidprv:keycode="KEYCODE_M" androidprv:modifierState="META" /> <bookmark - category="android.intent.category.APP_MUSIC" - androidprv:keycode="KEYCODE_P" - androidprv:modifierState="META" /> - <bookmark - role="android.app.role.SMS" - androidprv:keycode="KEYCODE_S" - androidprv:modifierState="META" /> - <bookmark category="android.intent.category.APP_CALCULATOR" androidprv:keycode="KEYCODE_U" androidprv:modifierState="META" /> diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java index cf5323e1f3a5..35b077e30f12 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java @@ -26,7 +26,6 @@ import static android.view.KeyEvent.KEYCODE_E; import static android.view.KeyEvent.KEYCODE_ENTER; import static android.view.KeyEvent.KEYCODE_H; import static android.view.KeyEvent.KEYCODE_J; -import static android.view.KeyEvent.KEYCODE_K; import static android.view.KeyEvent.KEYCODE_M; import static android.view.KeyEvent.KEYCODE_META_LEFT; import static android.view.KeyEvent.KEYCODE_N; @@ -67,14 +66,12 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { // These shortcuts should align with those defined in // services/tests/wmtests/res/xml/bookmarks.xml INTENT_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR); - INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS); + INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_CONTACTS); INTENT_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL); - INTENT_SHORTCUTS.append(KEYCODE_K, Intent.CATEGORY_APP_CALENDAR); + INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CALENDAR); INTENT_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS); - INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC); ROLE_SHORTCUTS.append(KEYCODE_B, RoleManager.ROLE_BROWSER); - ROLE_SHORTCUTS.append(KEYCODE_S, RoleManager.ROLE_SMS); } private static final int ANY_DISPLAY_ID = 123; @@ -109,7 +106,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_B}, 0); mPhoneWindowManager.assertLaunchRole(RoleManager.ROLE_BROWSER); - sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_C}, 0); + sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_P}, 0); mPhoneWindowManager.assertLaunchCategory(Intent.CATEGORY_APP_CONTACTS); sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_J}, 0); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 584c4c96ae1d..87db6c0e8577 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -4899,6 +4899,8 @@ public class SizeCompatTests extends WindowTestsBase { prepareLimitedBounds(mActivity, maxAspect, minAspect, ActivityInfo.SCREEN_ORIENTATION_NOSENSOR, true /* isUnresizable */); + assertTrue(ActivityRecord.canBeUniversalResizeable(mActivity.info.applicationInfo, + mWm, true /* isLargeScreen */, false /* forActivity */)); assertTrue(mActivity.isUniversalResizeable()); assertTrue(mActivity.isResizeable()); assertFalse(mActivity.shouldCreateAppCompatDisplayInsets()); @@ -4953,6 +4955,8 @@ public class SizeCompatTests extends WindowTestsBase { .setComponent(getUniqueComponentName(mContext.getPackageName())) .setTask(mTask).build(); assertFalse(optOutAppActivity.isUniversalResizeable()); + assertFalse(ActivityRecord.canBeUniversalResizeable(mActivity.info.applicationInfo, + mWm, true /* isLargeScreen */, false /* forActivity */)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 8bbba1b17240..a425401c6238 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -42,7 +42,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; @@ -85,7 +84,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; -import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -97,7 +95,6 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.Surface; import android.view.SurfaceControl; import android.view.View; import android.view.WindowInsets; @@ -1192,55 +1189,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { argThat(h -> (h.inputConfig & InputConfig.SENSITIVE_FOR_PRIVACY) != 0)); } - @RequiresFlagsDisabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - @Test - public void testDrawMagnifiedViewport() { - final int displayId = mDisplayContent.mDisplayId; - // Use real surface, so ViewportWindow's BlastBufferQueue can be created. - final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>(); - mWm.mSurfaceControlFactory = () -> new SurfaceControl.Builder() { - @Override - public SurfaceControl build() { - final SurfaceControl sc = super.build(); - surfaceControls.add(sc); - return sc; - } - }; - mWm.mAccessibilityController.setMagnificationCallbacks(displayId, - mock(WindowManagerInternal.MagnificationCallbacks.class)); - final boolean[] lockCanvasInWmLock = { false }; - final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId); - spyOn(surface); - doAnswer(invocationOnMock -> { - lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock); - invocationOnMock.callRealMethod(); - return null; - }).when(surface).lockCanvas(any()); - mWm.mAccessibilityController - .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId); - waitUntilHandlersIdle(); - try { - verify(surface).lockCanvas(any()); - - clearInvocations(surface); - // Invalidate and redraw. - mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent); - mWm.mAccessibilityController - .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId); - // Turn off magnification to release surface. - mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null); - waitUntilHandlersIdle(); - // lockCanvas must not be called after releasing. - verify(surface, never()).lockCanvas(any()); - verify(surface).release(); - assertFalse(lockCanvasInWmLock[0]); - } finally { - for (int i = surfaceControls.size() - 1; i >= 0; --i) { - surfaceControls.get(i).release(); - } - } - } - @Test public void testRequestKeyboardShortcuts_noWindow() { doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString()); diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 5ef0fe346dc6..d976f5ba1af2 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -337,7 +337,7 @@ public final class UsbAlsaManager { deviceAddress, hasOutput, hasInput, isInputHeadset, isOutputHeadset, isDock); alsaDevice.setDeviceNameAndDescription( - cardRec.getCardName(), cardRec.getCardDescription()); + usbDevice.getProductName(), cardRec.getCardDescription()); if (IS_MULTI_MODE) { deselectCurrentDevice(alsaDevice.getInputDeviceType()); deselectCurrentDevice(alsaDevice.getOutputDeviceType()); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index b739666c3f1b..131f46bc790e 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -3168,7 +3168,7 @@ interface ITelephony { */ boolean setSatelliteAccessControlOverlayConfigs(in boolean reset, in boolean isAllowed, in String s2CellFile, in long locationFreshDurationNanos, - in List<String> satelliteCountryCodes); + in List<String> satelliteCountryCodes, String satelliteAccessConfigurationFile); /** * This API can be used in only testing to override oem-enabled satellite provision status. diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java index bfce3d276a2d..6d818d7287b0 100644 --- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java +++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java @@ -159,7 +159,7 @@ public class BatteryUsageStatsPerfTest { private static BatteryUsageStats buildBatteryUsageStats() { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false, false, false, 0) + new BatteryUsageStats.Builder(new String[]{"FOO"}, false, false, false, 0) .setBatteryCapacity(4000) .setDischargePercentage(20) .setDischargedPowerRange(1000, 2000) @@ -182,8 +182,7 @@ public class BatteryUsageStatsPerfTest { .setTimeInProcessStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000); for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; componentId++) { - consumerBuilder.addConsumedPower(componentId, componentId * 123.0, - BatteryConsumer.POWER_MODEL_POWER_PROFILE); + consumerBuilder.addConsumedPower(componentId, componentId * 123.0); consumerBuilder.addUsageDurationMillis(componentId, componentId * 1000); } diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp index e14e5fea001f..1c8386add1b1 100644 --- a/tests/BinaryTransparencyHostTest/Android.bp +++ b/tests/BinaryTransparencyHostTest/Android.bp @@ -31,6 +31,8 @@ java_test_host { ], static_libs: [ "truth", + "flag-junit-host", + "android.app.flags-aconfig-java-host", ], device_common_data: [ ":BinaryTransparencyTestApp", diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java index 6e5f08a11ed8..6d8dbcb5c963 100644 --- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java +++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java @@ -24,6 +24,9 @@ import static org.junit.Assert.fail; import android.platform.test.annotations.LargeTest; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.host.HostFlagsValueProvider; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.log.LogUtil.CLog; @@ -34,6 +37,7 @@ import com.android.tradefed.util.CommandResult; import com.android.tradefed.util.CommandStatus; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +53,10 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { /** Waiting time for the job to be scheduled */ private static final int JOB_CREATION_MAX_SECONDS = 30; + @Rule + public final CheckFlagsRule mCheckFlagsRule = + HostFlagsValueProvider.createCheckFlagsRule(this::getDevice); + @Before public void setUp() throws Exception { cancelPendingJob(); @@ -123,6 +131,7 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { } } + @RequiresFlagsDisabled(android.app.Flags.FLAG_BACKGROUND_INSTALL_CONTROL_CALLBACK_API) @Test public void testPreloadUpdateTriggersJobScheduling() throws Exception { try { diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt deleted file mode 100644 index 6573c2c83f20..000000000000 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.helpers - -import android.app.Instrumentation -import android.content.Intent -import android.tools.traces.parsers.toFlickerComponent -import com.android.server.wm.flicker.testapp.ActivityOptions - -class BottomHalfPipAppHelper( - instrumentation: Instrumentation, - private val useLaunchingActivity: Boolean = false, -) : PipAppHelper( - instrumentation, - appName = ActivityOptions.BottomHalfPip.LABEL, - componentNameMatcher = ActivityOptions.BottomHalfPip.COMPONENT - .toFlickerComponent() -) { - override val openAppIntent: Intent - get() = super.openAppIntent.apply { - component = if (useLaunchingActivity) { - ActivityOptions.BottomHalfPip.LAUNCHING_APP_COMPONENT - } else { - ActivityOptions.BottomHalfPip.COMPONENT - } - } -}
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt index e0900a64c52f..c1c5dc66bac1 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt @@ -39,6 +39,7 @@ import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod.TOUCH import java.time.Duration +import kotlin.math.abs /** * Wrapper class around App helper classes. This class adds functionality to the apps that the @@ -222,11 +223,16 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : val expectedRect = Rect(displayRect).apply { if (toLeft) right -= expectedWidth else left += expectedWidth } - - wmHelper - .StateSyncBuilder() + wmHelper.StateSyncBuilder() .withAppTransitionIdle() - .withSurfaceVisibleRegion(this, Region(expectedRect)) + .withSurfaceMatchingVisibleRegion( + this, + Region(expectedRect), + { surfaceRegion, expectedRegion -> + areSnapWindowRegionsMatchingWithinThreshold( + surfaceRegion, expectedRegion, toLeft + ) + }) .waitForAndVerify() } @@ -460,6 +466,33 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : private fun isInDesktopWindowingMode(wmHelper: WindowManagerStateHelper) = wmHelper.getWindow(innerHelper)?.windowingMode == WINDOWING_MODE_FREEFORM + private fun areSnapWindowRegionsMatchingWithinThreshold( + surfaceRegion: Region, expectedRegion: Region, toLeft: Boolean + ): Boolean { + val surfaceBounds = surfaceRegion.bounds + val expectedBounds = expectedRegion.bounds + // If snapped to left, right bounds will be cut off by the center divider. + // Else if snapped to right, the left bounds will be cut off. + val leftSideMatching: Boolean + val rightSideMatching: Boolean + if (toLeft) { + leftSideMatching = surfaceBounds.left == expectedBounds.left + rightSideMatching = + abs(surfaceBounds.right - expectedBounds.right) <= + surfaceBounds.right * SNAP_WINDOW_MAX_THRESHOLD_DIFF + } else { + leftSideMatching = + abs(surfaceBounds.left - expectedBounds.left) <= + surfaceBounds.left * SNAP_WINDOW_MAX_THRESHOLD_DIFF + rightSideMatching = surfaceBounds.right == expectedBounds.right + } + + return surfaceBounds.top == expectedBounds.top && + surfaceBounds.bottom == expectedBounds.bottom && + leftSideMatching && + rightSideMatching + } + private companion object { val TIMEOUT: Duration = Duration.ofSeconds(3) const val SNAP_RESIZE_DRAG_INSET: Int = 5 // inset to avoid dragging to display edge @@ -475,5 +508,12 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : const val HEADER_EMPTY_VIEW: String = "caption_handle" val caption: BySelector get() = By.res(SYSTEMUI_PACKAGE, CAPTION) + // In DesktopMode, window snap can be done with just a single window. In this case, the + // divider tiling between left and right window won't be shown, and hence its states are not + // obtainable in test. + // As the test should just focus on ensuring window goes to one side of the screen, an + // acceptable approach is to ensure snapped window still fills > 95% of either side of the + // screen. + const val SNAP_WINDOW_MAX_THRESHOLD_DIFF = 0.05 } } diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt index 2fe84b00b040..db4838ee6092 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -27,7 +27,6 @@ import android.tools.device.apphelpers.StandardAppHelper import android.tools.helpers.FIND_TIMEOUT import android.tools.helpers.SYSTEMUI_PACKAGE import android.tools.traces.ConditionsFactory -import android.tools.traces.component.ComponentNameMatcher import android.tools.traces.component.IComponentMatcher import android.tools.traces.parsers.WindowManagerStateHelper import android.tools.traces.parsers.toFlickerComponent @@ -36,11 +35,12 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -open class PipAppHelper( - instrumentation: Instrumentation, - appName: String = ActivityOptions.Pip.LABEL, - componentNameMatcher: ComponentNameMatcher = ActivityOptions.Pip.COMPONENT.toFlickerComponent(), -) : StandardAppHelper(instrumentation, appName, componentNameMatcher) { +open class PipAppHelper(instrumentation: Instrumentation) : + StandardAppHelper( + instrumentation, + ActivityOptions.Pip.LABEL, + ActivityOptions.Pip.COMPONENT.toFlickerComponent() + ) { private val mediaSessionManager: MediaSessionManager get() = context.getSystemService(MediaSessionManager::class.java) diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 7c24a4adca3d..9ce8e807f612 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -347,27 +347,6 @@ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> </intent-filter> </activity> - <activity android:name=".BottomHalfPipLaunchingActivity" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" - android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity" - android:theme="@style/CutoutShortEdges" - android:label="BottomHalfPipLaunchingActivity" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - <activity - android:name=".BottomHalfPipActivity" - android:resizeableActivity="true" - android:supportsPictureInPicture="true" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" - android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity" - android:theme="@style/TranslucentTheme" - android:label="BottomHalfPipActivity" - android:exported="true"> - </activity> <activity android:name=".SplitScreenActivity" android:resizeableActivity="true" android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml index 837d050b73ff..47d113717ae0 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml @@ -62,12 +62,6 @@ <item name="android:backgroundDimEnabled">false</item> </style> - <style name="TranslucentTheme" parent="@style/OptOutEdgeToEdge"> - <item name="android:windowIsTranslucent">true</item> - <item name="android:windowContentOverlay">@null</item> - <item name="android:backgroundDimEnabled">false</item> - </style> - <style name="no_starting_window" parent="@style/OptOutEdgeToEdge"> <item name="android:windowDisablePreview">true</item> </style> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 0c1ac9951d32..73625da9dfa5 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -241,21 +241,6 @@ public class ActivityOptions { FLICKER_APP_PACKAGE + ".PipActivity"); } - public static class BottomHalfPip { - public static final String LAUNCHING_APP_LABEL = "BottomHalfPipLaunchingActivity"; - // Test App > Bottom Half PIP Activity - public static final String LABEL = "BottomHalfPipActivity"; - - // Use the bottom half layout for PIP Activity - public static final String EXTRA_BOTTOM_HALF_LAYOUT = "bottom_half"; - - public static final ComponentName LAUNCHING_APP_COMPONENT = new ComponentName( - FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".BottomHalfPipLaunchingActivity"); - - public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, - FLICKER_APP_PACKAGE + ".BottomHalfPipActivity"); - } - public static class SplitScreen { public static class Primary { public static final String LABEL = "SplitScreenPrimaryActivity"; diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java deleted file mode 100644 index 3d4865572486..000000000000 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.testapp; - -import android.app.Activity; -import android.content.res.Configuration; -import android.os.Bundle; -import android.view.ViewGroup.LayoutParams; -import android.view.WindowManager; - -import androidx.annotation.NonNull; - -public class BottomHalfPipActivity extends PipActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setTheme(R.style.TranslucentTheme); - updateLayout(); - } - - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - updateLayout(); - } - - /** - * Sets to match parent layout if the activity is - * {@link Activity#isInPictureInPictureMode()}. Otherwise, set to bottom half - * layout. - * - * @see #setToBottomHalfMode(boolean) - */ - private void updateLayout() { - setToBottomHalfMode(!isInPictureInPictureMode()); - } - - /** - * Sets `useBottomHalfLayout` to `true` to use the bottom half layout. Use the - * [LayoutParams.MATCH_PARENT] layout. - */ - private void setToBottomHalfMode(boolean useBottomHalfLayout) { - final WindowManager.LayoutParams attrs = getWindow().getAttributes(); - if (useBottomHalfLayout) { - final int taskHeight = getWindowManager().getCurrentWindowMetrics().getBounds() - .height(); - attrs.y = taskHeight / 2; - attrs.height = taskHeight / 2; - } else { - attrs.y = 0; - attrs.height = LayoutParams.MATCH_PARENT; - } - getWindow().setAttributes(attrs); - } -} diff --git a/tests/Input/res/xml/bookmarks.xml b/tests/Input/res/xml/bookmarks.xml index a4c898d8159a..68ec1233cdd7 100644 --- a/tests/Input/res/xml/bookmarks.xml +++ b/tests/Input/res/xml/bookmarks.xml @@ -23,7 +23,7 @@ androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_CONTACTS" - androidprv:keycode="KEYCODE_C" + androidprv:keycode="KEYCODE_P" androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_EMAIL" @@ -31,21 +31,13 @@ androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_CALENDAR" - androidprv:keycode="KEYCODE_K" + androidprv:keycode="KEYCODE_C" androidprv:modifierState="META" /> <bookmark category="android.intent.category.APP_MAPS" androidprv:keycode="KEYCODE_M" androidprv:modifierState="META" /> <bookmark - category="android.intent.category.APP_MUSIC" - androidprv:keycode="KEYCODE_P" - androidprv:modifierState="META" /> - <bookmark - role="android.app.role.SMS" - androidprv:keycode="KEYCODE_S" - androidprv:modifierState="META" /> - <bookmark category="android.intent.category.APP_CALCULATOR" androidprv:keycode="KEYCODE_U" androidprv:modifierState="META" /> @@ -57,7 +49,7 @@ <bookmark category="android.intent.category.APP_CONTACTS" - androidprv:keycode="KEYCODE_C" + androidprv:keycode="KEYCODE_P" shift="true" /> <bookmark @@ -65,4 +57,4 @@ class="com.test.BookmarkTest" androidprv:keycode="KEYCODE_J" shift="true" /> -</bookmarks>
\ No newline at end of file +</bookmarks> diff --git a/tests/Input/res/xml/bookmarks_legacy.xml b/tests/Input/res/xml/bookmarks_legacy.xml index 8bacf490ad9e..78cc48b19416 100644 --- a/tests/Input/res/xml/bookmarks_legacy.xml +++ b/tests/Input/res/xml/bookmarks_legacy.xml @@ -22,23 +22,17 @@ shortcut="b" /> <bookmark category="android.intent.category.APP_CONTACTS" - shortcut="c" /> + shortcut="p" /> <bookmark category="android.intent.category.APP_EMAIL" shortcut="e" /> <bookmark category="android.intent.category.APP_CALENDAR" - shortcut="k" /> + shortcut="c" /> <bookmark category="android.intent.category.APP_MAPS" shortcut="m" /> <bookmark - category="android.intent.category.APP_MUSIC" - shortcut="p" /> - <bookmark - role="android.app.role.SMS" - shortcut="s" /> - <bookmark category="android.intent.category.APP_CALCULATOR" shortcut="u" /> @@ -49,7 +43,7 @@ <bookmark category="android.intent.category.APP_CONTACTS" - shortcut="c" + shortcut="p" shift="true" /> <bookmark @@ -57,4 +51,4 @@ class="com.test.BookmarkTest" shortcut="j" shift="true" /> -</bookmarks>
\ No newline at end of file +</bookmarks> diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 36a89f95aa6f..662b8e562126 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -542,9 +542,9 @@ class KeyGestureControllerTests { ), TestData( "META + C -> Launch Default Contacts", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C), + intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_C), + intArrayOf(KeyEvent.KEYCODE_P), KeyEvent.META_META_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS) @@ -560,9 +560,9 @@ class KeyGestureControllerTests { ), TestData( "META + K -> Launch Default Calendar", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K), + intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_K), + intArrayOf(KeyEvent.KEYCODE_C), KeyEvent.META_META_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR) @@ -577,24 +577,6 @@ class KeyGestureControllerTests { AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS) ), TestData( - "META + P -> Launch Default Music", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P), - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_P), - KeyEvent.META_META_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), - AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC) - ), - TestData( - "META + S -> Launch Default SMS", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S), - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_S), - KeyEvent.META_META_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), - AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS) - ), - TestData( "META + U -> Launch Default Calculator", intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, @@ -839,10 +821,10 @@ class KeyGestureControllerTests { AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER) ), TestData( - "META + C -> Launch Default Contacts", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C), + "META + P -> Launch Default Contacts", + intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_C), + intArrayOf(KeyEvent.KEYCODE_P), KeyEvent.META_META_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS) @@ -857,10 +839,10 @@ class KeyGestureControllerTests { AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL) ), TestData( - "META + K -> Launch Default Calendar", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K), + "META + C -> Launch Default Calendar", + intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_K), + intArrayOf(KeyEvent.KEYCODE_C), KeyEvent.META_META_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR) @@ -875,24 +857,6 @@ class KeyGestureControllerTests { AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS) ), TestData( - "META + P -> Launch Default Music", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P), - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_P), - KeyEvent.META_META_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), - AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC) - ), - TestData( - "META + S -> Launch Default SMS", - intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S), - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_S), - KeyEvent.META_META_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), - AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS) - ), - TestData( "META + U -> Launch Default Calculator", intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, @@ -915,14 +879,14 @@ class KeyGestureControllerTests { AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER) ), TestData( - "META + SHIFT + C -> Launch Default Contacts", + "META + SHIFT + P -> Launch Default Contacts", intArrayOf( KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_SHIFT_LEFT, - KeyEvent.KEYCODE_C + KeyEvent.KEYCODE_P ), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - intArrayOf(KeyEvent.KEYCODE_C), + intArrayOf(KeyEvent.KEYCODE_P), KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS) @@ -1627,4 +1591,4 @@ class KeyGestureControllerTests { return true } } -}
\ No newline at end of file +} diff --git a/tests/broadcasts/unit/Android.bp b/tests/broadcasts/unit/Android.bp new file mode 100644 index 000000000000..9e15ac41d84b --- /dev/null +++ b/tests/broadcasts/unit/Android.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_framework_backstage_power", +} + +android_test { + name: "BroadcastUnitTests", + srcs: ["src/**/*.java"], + defaults: [ + "modules-utils-extended-mockito-rule-defaults", + ], + static_libs: [ + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-extended-minus-junit4", + "truth", + "flag-junit", + "android.app.flags-aconfig-java", + "junit-params", + ], + certificate: "platform", + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/tests/broadcasts/unit/AndroidManifest.xml b/tests/broadcasts/unit/AndroidManifest.xml new file mode 100644 index 000000000000..61eb230f7957 --- /dev/null +++ b/tests/broadcasts/unit/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.broadcasts.unit" > + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.broadcasts.unit" + android:label="Broadcasts Unit Tests"/> +</manifest>
\ No newline at end of file diff --git a/tests/broadcasts/unit/AndroidTest.xml b/tests/broadcasts/unit/AndroidTest.xml new file mode 100644 index 000000000000..b91e4783b69e --- /dev/null +++ b/tests/broadcasts/unit/AndroidTest.xml @@ -0,0 +1,29 @@ +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Broadcasts tests"> + <option name="test-suite-tag" value="apct" /> + <option name="test-tag" value="BroadcastUnitTests" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="BroadcastUnitTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.broadcasts.unit" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration>
\ No newline at end of file diff --git a/tests/broadcasts/unit/OWNERS b/tests/broadcasts/unit/OWNERS new file mode 100644 index 000000000000..f1e450b7e5f9 --- /dev/null +++ b/tests/broadcasts/unit/OWNERS @@ -0,0 +1,2 @@ +# Bug component: 316181 +include platform/frameworks/base:/BROADCASTS_OWNERS
\ No newline at end of file diff --git a/tests/broadcasts/unit/TEST_MAPPING b/tests/broadcasts/unit/TEST_MAPPING new file mode 100644 index 000000000000..b920e2586c86 --- /dev/null +++ b/tests/broadcasts/unit/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "BroadcastUnitTests" + } + ] +} diff --git a/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java new file mode 100644 index 000000000000..ad032fb2fba6 --- /dev/null +++ b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.os.IpcDataCache; +import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.annotations.Keep; +import com.android.modules.utils.testing.ExtendedMockitoRule; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +@RunWith(JUnitParamsRunner.class) +public class BroadcastStickyCacheTest { + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this) + .mockStatic(IpcDataCache.class) + .mockStatic(ActivityManager.class) + .build(); + + @Mock + private IActivityManager mActivityManagerMock; + + @Mock + private IApplicationThread mIApplicationThreadMock; + + @Keep + private static Object stickyBroadcastList() { + return BroadcastStickyCache.STICKY_BROADCAST_ACTIONS; + } + + @Before + public void setUp() { + BroadcastStickyCache.clearCacheForTest(); + + doNothing().when(() -> IpcDataCache.invalidateCache(anyString(), anyString())); + } + + @Test + @DisableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE) + public void useCache_flagDisabled_returnsFalse() { + assertFalse(BroadcastStickyCache.useCache(new IntentFilter(Intent.ACTION_BATTERY_CHANGED))); + } + + @Test + @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE) + public void useCache_nullFilter_returnsFalse() { + assertFalse(BroadcastStickyCache.useCache(null)); + } + + @Test + @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE) + public void useCache_filterWithoutAction_returnsFalse() { + assertFalse(BroadcastStickyCache.useCache(new IntentFilter())); + } + + @Test + @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE) + public void useCache_filterWithoutStickyBroadcastAction_returnsFalse() { + assertFalse(BroadcastStickyCache.useCache(new IntentFilter(Intent.ACTION_BOOT_COMPLETED))); + } + + @Test + @DisableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE) + public void invalidateCache_flagDisabled_cacheNotInvalidated() { + final String apiName = BroadcastStickyCache.sActionApiNameMap.get( + AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); + + BroadcastStickyCache.invalidateCache( + AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); + + ExtendedMockito.verify( + () -> IpcDataCache.invalidateCache(eq(IpcDataCache.MODULE_SYSTEM), eq(apiName)), + times(0)); + } + + @Test + @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE) + public void invalidateCache_broadcastNotSticky_cacheNotInvalidated() { + BroadcastStickyCache.invalidateCache(Intent.ACTION_AIRPLANE_MODE_CHANGED); + + ExtendedMockito.verify( + () -> IpcDataCache.invalidateCache(eq(IpcDataCache.MODULE_SYSTEM), anyString()), + times(0)); + } + + @Test + @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE) + public void invalidateCache_withStickyBroadcast_cacheInvalidated() { + final String apiName = BroadcastStickyCache.sActionApiNameMap.get( + Intent.ACTION_BATTERY_CHANGED); + + BroadcastStickyCache.invalidateCache(Intent.ACTION_BATTERY_CHANGED); + + ExtendedMockito.verify( + () -> IpcDataCache.invalidateCache(eq(IpcDataCache.MODULE_SYSTEM), eq(apiName)), + times(1)); + } + + @Test + public void invalidateAllCaches_cacheInvalidated() { + BroadcastStickyCache.invalidateAllCaches(); + + for (int i = BroadcastStickyCache.sActionApiNameMap.size() - 1; i > -1; i--) { + final String apiName = BroadcastStickyCache.sActionApiNameMap.valueAt(i); + ExtendedMockito.verify(() -> IpcDataCache.invalidateCache(anyString(), + eq(apiName)), times(1)); + } + } + + @Test + @Parameters(method = "stickyBroadcastList") + public void getIntent_createNewCache_verifyRegisterReceiverIsCalled(String action) + throws RemoteException { + setActivityManagerMock(action); + final IntentFilter filter = new IntentFilter(action); + final Intent intent = queryIntent(filter); + + assertNotNull(intent); + assertEquals(intent.getAction(), action); + verify(mActivityManagerMock, times(1)).registerReceiverWithFeature( + eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(), + eq(filter), anyString(), anyInt(), anyInt()); + } + + @Test + public void getIntent_querySameValueTwice_verifyRegisterReceiverIsCalledOnce() + throws RemoteException { + setActivityManagerMock(Intent.ACTION_DEVICE_STORAGE_LOW); + final Intent intent = queryIntent(new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW)); + final Intent cachedIntent = queryIntent(new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW)); + + assertNotNull(intent); + assertEquals(intent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW); + assertNotNull(cachedIntent); + assertEquals(cachedIntent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW); + + verify(mActivityManagerMock, times(1)).registerReceiverWithFeature( + eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(), + any(), anyString(), anyInt(), anyInt()); + } + + @Test + public void getIntent_querySameActionWithDifferentFilter_verifyRegisterReceiverCalledTwice() + throws RemoteException { + setActivityManagerMock(Intent.ACTION_DEVICE_STORAGE_LOW); + final IntentFilter filter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); + final Intent intent = queryIntent(filter); + + final IntentFilter newFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); + newFilter.addDataScheme("file"); + final Intent newIntent = queryIntent(newFilter); + + assertNotNull(intent); + assertEquals(intent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW); + assertNotNull(newIntent); + assertEquals(newIntent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW); + + verify(mActivityManagerMock, times(1)).registerReceiverWithFeature( + eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(), + eq(filter), anyString(), anyInt(), anyInt()); + + verify(mActivityManagerMock, times(1)).registerReceiverWithFeature( + eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(), + eq(newFilter), anyString(), anyInt(), anyInt()); + } + + private Intent queryIntent(IntentFilter filter) { + return BroadcastStickyCache.getIntent( + mIApplicationThreadMock, + "android", + "android", + filter, + "system", + 0, + 0 + ); + } + + private void setActivityManagerMock(String action) throws RemoteException { + when(ActivityManager.getService()).thenReturn(mActivityManagerMock); + when(mActivityManagerMock.registerReceiverWithFeature(any(), anyString(), + anyString(), anyString(), any(), any(), anyString(), anyInt(), + anyInt())).thenReturn(new Intent(action)); + } +} |